Rip out activerecord-tenanting
Co-authored-by: Stanko K.R. <stanko@stanko.io>
This commit is contained in:
@@ -10,7 +10,6 @@ gem "stimulus-rails"
|
|||||||
gem "turbo-rails"
|
gem "turbo-rails"
|
||||||
|
|
||||||
# Deployment and drivers
|
# Deployment and drivers
|
||||||
gem "activerecord-tenanted", bc: "activerecord-tenanted"
|
|
||||||
gem "bootsnap", require: false
|
gem "bootsnap", require: false
|
||||||
gem "kamal", require: false
|
gem "kamal", require: false
|
||||||
gem "puma", ">= 5.0"
|
gem "puma", ">= 5.0"
|
||||||
|
|||||||
+20
-30
@@ -1,12 +1,3 @@
|
|||||||
GIT
|
|
||||||
remote: https://github.com/basecamp/activerecord-tenanted
|
|
||||||
revision: 1e92cb34d0b7b02875040826330c1918abb06e9d
|
|
||||||
specs:
|
|
||||||
activerecord-tenanted (0.6.0)
|
|
||||||
activerecord (>= 8.1.beta)
|
|
||||||
railties (>= 8.1.beta)
|
|
||||||
zeitwerk
|
|
||||||
|
|
||||||
GIT
|
GIT
|
||||||
remote: https://github.com/basecamp/queenbee-plugin
|
remote: https://github.com/basecamp/queenbee-plugin
|
||||||
revision: eb01c697de1ad028afc65cc7d9b5345a7a8e849f
|
revision: eb01c697de1ad028afc65cc7d9b5345a7a8e849f
|
||||||
@@ -161,10 +152,10 @@ GEM
|
|||||||
anyway_config (2.7.2)
|
anyway_config (2.7.2)
|
||||||
ruby-next-core (~> 1.0)
|
ruby-next-core (~> 1.0)
|
||||||
ast (2.4.3)
|
ast (2.4.3)
|
||||||
autotuner (1.1.0)
|
autotuner (1.0.2)
|
||||||
aws-eventstream (1.4.0)
|
aws-eventstream (1.4.0)
|
||||||
aws-partitions (1.1181.0)
|
aws-partitions (1.1175.0)
|
||||||
aws-sdk-core (3.236.0)
|
aws-sdk-core (3.234.0)
|
||||||
aws-eventstream (~> 1, >= 1.3.0)
|
aws-eventstream (~> 1, >= 1.3.0)
|
||||||
aws-partitions (~> 1, >= 1.992.0)
|
aws-partitions (~> 1, >= 1.992.0)
|
||||||
aws-sigv4 (~> 1.9)
|
aws-sigv4 (~> 1.9)
|
||||||
@@ -172,10 +163,10 @@ GEM
|
|||||||
bigdecimal
|
bigdecimal
|
||||||
jmespath (~> 1, >= 1.6.1)
|
jmespath (~> 1, >= 1.6.1)
|
||||||
logger
|
logger
|
||||||
aws-sdk-kms (1.117.0)
|
aws-sdk-kms (1.115.0)
|
||||||
aws-sdk-core (~> 3, >= 3.234.0)
|
aws-sdk-core (~> 3, >= 3.234.0)
|
||||||
aws-sigv4 (~> 1.5)
|
aws-sigv4 (~> 1.5)
|
||||||
aws-sdk-s3 (1.203.0)
|
aws-sdk-s3 (1.201.0)
|
||||||
aws-sdk-core (~> 3, >= 3.234.0)
|
aws-sdk-core (~> 3, >= 3.234.0)
|
||||||
aws-sdk-kms (~> 1)
|
aws-sdk-kms (~> 1)
|
||||||
aws-sigv4 (~> 1.5)
|
aws-sigv4 (~> 1.5)
|
||||||
@@ -189,7 +180,7 @@ GEM
|
|||||||
bindex (0.8.1)
|
bindex (0.8.1)
|
||||||
bootsnap (1.18.6)
|
bootsnap (1.18.6)
|
||||||
msgpack (~> 1.2)
|
msgpack (~> 1.2)
|
||||||
brakeman (7.1.1)
|
brakeman (7.1.0)
|
||||||
racc
|
racc
|
||||||
builder (3.3.0)
|
builder (3.3.0)
|
||||||
bundler-audit (0.9.2)
|
bundler-audit (0.9.2)
|
||||||
@@ -259,7 +250,7 @@ GEM
|
|||||||
actionview (>= 7.0.0)
|
actionview (>= 7.0.0)
|
||||||
activesupport (>= 7.0.0)
|
activesupport (>= 7.0.0)
|
||||||
jmespath (1.6.2)
|
jmespath (1.6.2)
|
||||||
json (2.16.0)
|
json (2.15.2)
|
||||||
jwt (3.1.2)
|
jwt (3.1.2)
|
||||||
base64
|
base64
|
||||||
kamal (2.8.2)
|
kamal (2.8.2)
|
||||||
@@ -299,7 +290,7 @@ GEM
|
|||||||
logger
|
logger
|
||||||
mini_mime (1.1.5)
|
mini_mime (1.1.5)
|
||||||
mini_portile2 (2.8.9)
|
mini_portile2 (2.8.9)
|
||||||
minitest (5.26.1)
|
minitest (5.26.0)
|
||||||
mission_control-jobs (1.1.0)
|
mission_control-jobs (1.1.0)
|
||||||
actioncable (>= 7.1)
|
actioncable (>= 7.1)
|
||||||
actionpack (>= 7.1)
|
actionpack (>= 7.1)
|
||||||
@@ -417,7 +408,7 @@ GEM
|
|||||||
tsort
|
tsort
|
||||||
redcarpet (3.6.1)
|
redcarpet (3.6.1)
|
||||||
regexp_parser (2.11.3)
|
regexp_parser (2.11.3)
|
||||||
reline (0.6.3)
|
reline (0.6.2)
|
||||||
io-console (~> 0.5)
|
io-console (~> 0.5)
|
||||||
rexml (3.4.4)
|
rexml (3.4.4)
|
||||||
rouge (4.6.1)
|
rouge (4.6.1)
|
||||||
@@ -467,10 +458,10 @@ GEM
|
|||||||
rexml (~> 3.2, >= 3.2.5)
|
rexml (~> 3.2, >= 3.2.5)
|
||||||
rubyzip (>= 1.2.2, < 4.0)
|
rubyzip (>= 1.2.2, < 4.0)
|
||||||
websocket (~> 1.0)
|
websocket (~> 1.0)
|
||||||
sentry-rails (6.1.0)
|
sentry-rails (6.0.0)
|
||||||
railties (>= 5.2.0)
|
railties (>= 5.2.0)
|
||||||
sentry-ruby (~> 6.1.0)
|
sentry-ruby (~> 6.0.0)
|
||||||
sentry-ruby (6.1.0)
|
sentry-ruby (6.0.0)
|
||||||
bigdecimal
|
bigdecimal
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
sniffer (0.5.0)
|
sniffer (0.5.0)
|
||||||
@@ -481,23 +472,23 @@ GEM
|
|||||||
activejob (>= 7.2)
|
activejob (>= 7.2)
|
||||||
activerecord (>= 7.2)
|
activerecord (>= 7.2)
|
||||||
railties (>= 7.2)
|
railties (>= 7.2)
|
||||||
solid_cache (1.0.10)
|
solid_cache (1.0.8)
|
||||||
activejob (>= 7.2)
|
activejob (>= 7.2)
|
||||||
activerecord (>= 7.2)
|
activerecord (>= 7.2)
|
||||||
railties (>= 7.2)
|
railties (>= 7.2)
|
||||||
solid_queue (1.2.4)
|
solid_queue (1.2.2)
|
||||||
activejob (>= 7.1)
|
activejob (>= 7.1)
|
||||||
activerecord (>= 7.1)
|
activerecord (>= 7.1)
|
||||||
concurrent-ruby (>= 1.3.1)
|
concurrent-ruby (>= 1.3.1)
|
||||||
fugit (~> 1.11)
|
fugit (~> 1.11)
|
||||||
railties (>= 7.1)
|
railties (>= 7.1)
|
||||||
thor (>= 1.3.1)
|
thor (>= 1.3.1)
|
||||||
sqlite3 (2.8.0)
|
sqlite3 (2.7.4)
|
||||||
mini_portile2 (~> 2.8.0)
|
mini_portile2 (~> 2.8.0)
|
||||||
sqlite3 (2.8.0-arm64-darwin)
|
sqlite3 (2.7.4-arm64-darwin)
|
||||||
sqlite3 (2.8.0-x86_64-darwin)
|
sqlite3 (2.7.4-x86_64-darwin)
|
||||||
sqlite3 (2.8.0-x86_64-linux-gnu)
|
sqlite3 (2.7.4-x86_64-linux-gnu)
|
||||||
sqlite3 (2.8.0-x86_64-linux-musl)
|
sqlite3 (2.7.4-x86_64-linux-musl)
|
||||||
sshkit (1.24.0)
|
sshkit (1.24.0)
|
||||||
base64
|
base64
|
||||||
logger
|
logger
|
||||||
@@ -536,7 +527,7 @@ GEM
|
|||||||
web-push (3.0.2)
|
web-push (3.0.2)
|
||||||
jwt (~> 3.0)
|
jwt (~> 3.0)
|
||||||
openssl (~> 3.0)
|
openssl (~> 3.0)
|
||||||
webmock (3.26.1)
|
webmock (3.26.0)
|
||||||
addressable (>= 2.8.0)
|
addressable (>= 2.8.0)
|
||||||
crack (>= 0.3.2)
|
crack (>= 0.3.2)
|
||||||
hashdiff (>= 0.4.0, < 2.0.0)
|
hashdiff (>= 0.4.0, < 2.0.0)
|
||||||
@@ -586,7 +577,6 @@ PLATFORMS
|
|||||||
x86_64-linux-musl
|
x86_64-linux-musl
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
activerecord-tenanted!
|
|
||||||
activeresource
|
activeresource
|
||||||
autotuner
|
autotuner
|
||||||
aws-sdk-s3
|
aws-sdk-s3
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
class Card::AutoPostponeAllDueJob < ApplicationJob
|
class Card::AutoPostponeAllDueJob < ApplicationJob
|
||||||
def perform
|
def perform
|
||||||
ApplicationRecord.with_each_tenant do |tenant|
|
Card.auto_postpone_all_due
|
||||||
Card.auto_postpone_all_due
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
class DeleteUnusedTagsJob < ApplicationJob
|
class DeleteUnusedTagsJob < ApplicationJob
|
||||||
def perform
|
def perform
|
||||||
ApplicationRecord.with_each_tenant do |tenant|
|
Tag.unused.find_each do |tag|
|
||||||
Tag.unused.find_each do |tag|
|
tag.destroy!
|
||||||
tag.destroy!
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ class Notification::Bundle::DeliverAllJob < ApplicationJob
|
|||||||
queue_as :backend
|
queue_as :backend
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
ApplicationRecord.with_each_tenant do |tenant|
|
Notification::Bundle.deliver_all
|
||||||
Notification::Bundle.deliver_all
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,155 +0,0 @@
|
|||||||
require "find"
|
|
||||||
|
|
||||||
#
|
|
||||||
# This job backs up all the tenant databases using the SQLite Backup API, which should allow the
|
|
||||||
# application to continue running against the database while it is backed up.
|
|
||||||
#
|
|
||||||
# ref: https://www.sqlite.org/c3ref/backup_finish.html
|
|
||||||
#
|
|
||||||
# It will keep N files around, like this:
|
|
||||||
#
|
|
||||||
# storage/tenants/development/12345678/db/
|
|
||||||
# ├─ main.sqlite3
|
|
||||||
# ├─ main.sqlite3.1
|
|
||||||
# ├─ main.sqlite3.2
|
|
||||||
# ├─ main.sqlite3.3
|
|
||||||
# ├─ main.sqlite3.4
|
|
||||||
# └─ main.sqlite3.5
|
|
||||||
#
|
|
||||||
# On some systems, notably in production, we have an NFS-mounted filesystem into which the
|
|
||||||
# application copies the backup files for disaster recovery. We copy into an environment- and
|
|
||||||
# tenant-specific directory. The file, when copied, will be renamed with the timestamp of the file
|
|
||||||
# creation time. For example:
|
|
||||||
#
|
|
||||||
# /backups/production/12345678/main.sqlite3.20251014194804
|
|
||||||
#
|
|
||||||
# It will also clean up old backups in the NFS-mounted filesystem following our data retention policy.
|
|
||||||
#
|
|
||||||
class SQLiteBackupsJob < ApplicationJob
|
|
||||||
DEFAULT_NUMBER_OF_BACKUPS = 5
|
|
||||||
DEFAULT_STEP_PAGES = 1024
|
|
||||||
DEFAULT_SWEEP_DIR = "/backups"
|
|
||||||
DEFAULT_SWEEP_RETENTION = 30.days # https://37signals.com/policies/privacy
|
|
||||||
|
|
||||||
def perform(keep: DEFAULT_NUMBER_OF_BACKUPS, step: DEFAULT_STEP_PAGES, sweep_dir: DEFAULT_SWEEP_DIR, sweep_retention: DEFAULT_SWEEP_RETENTION)
|
|
||||||
@failures = []
|
|
||||||
|
|
||||||
ApplicationRecord.with_each_tenant do |tenant|
|
|
||||||
perform_file_rollover(tenant, keep:)
|
|
||||||
perform_backup(tenant, step:)
|
|
||||||
perform_sweep(tenant, sweep_dir:)
|
|
||||||
enforce_retention(tenant, sweep_dir:, sweep_retention:)
|
|
||||||
end
|
|
||||||
|
|
||||||
if @failures.present?
|
|
||||||
raise "SQLiteBackupsJob: failed to backup tenants: #{@failures.join(", ")}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
def perform_file_rollover(tenant, keep:)
|
|
||||||
keep.downto(2) do |j|
|
|
||||||
fresher = backup_path(tenant, j - 1)
|
|
||||||
staler = backup_path(tenant, j)
|
|
||||||
|
|
||||||
if j == keep && File.exist?(staler)
|
|
||||||
FileUtils.rm(staler)
|
|
||||||
end
|
|
||||||
|
|
||||||
if File.exist?(fresher)
|
|
||||||
# TODO: It may be worth benchmarking whether backing up into the previous backup is faster
|
|
||||||
# than backing up into an empty file.
|
|
||||||
FileUtils.mv(fresher, staler)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def perform_backup(tenant, step:)
|
|
||||||
ApplicationRecord.with_connection do |conn|
|
|
||||||
current_adapter = conn.raw_connection
|
|
||||||
backup_db = backup_path(tenant, 1)
|
|
||||||
backup_adapter = SQLite3::Database.new(backup_db)
|
|
||||||
backup = SQLite3::Backup.new(backup_adapter, "main", current_adapter, "main")
|
|
||||||
|
|
||||||
pages = 0
|
|
||||||
elapsed = ActiveSupport::Benchmark.realtime(:float_millisecond) do
|
|
||||||
loop do
|
|
||||||
status = backup.step(step)
|
|
||||||
case status
|
|
||||||
when SQLite3::Constants::ErrorCode::DONE
|
|
||||||
break
|
|
||||||
when SQLite3::Constants::ErrorCode::OK
|
|
||||||
total = backup.pagecount
|
|
||||||
progress = total - backup.remaining
|
|
||||||
log(tenant, :debug) { "Wrote #{progress} of #{total} pages." }
|
|
||||||
when SQLite3::Constants::ErrorCode::BUSY, SQLite3::Constants::ErrorCode::LOCKED
|
|
||||||
log(tenant, :debug) { "Busy, retrying." }
|
|
||||||
else
|
|
||||||
log(tenant, :error) { "Failed with status #{status}." }
|
|
||||||
@failures << tenant
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
pages = backup.pagecount
|
|
||||||
backup.finish
|
|
||||||
end
|
|
||||||
|
|
||||||
log(tenant) { sprintf("Backup complete in %<elapsed>.1f ms. Wrote %{pages} pages to %{path}", path: backup_db.inspect, pages: pages, elapsed: elapsed) }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def perform_sweep(tenant, sweep_dir:)
|
|
||||||
unless File.directory?(sweep_dir) && File.writable?(sweep_dir)
|
|
||||||
log(tenant, :warn) { "Skipping sweep, #{sweep_dir.inspect} does not exist or is not writable." }
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
backup_file = backup_path(tenant, 1)
|
|
||||||
sweep_path = File.join(sweep_dir, Rails.env, tenant)
|
|
||||||
FileUtils.mkdir_p(sweep_path)
|
|
||||||
|
|
||||||
if File.exist?(backup_file)
|
|
||||||
timestamp = File.ctime(backup_file).utc.strftime("%Y%m%d%H%M%S")
|
|
||||||
swept_file = File.join(sweep_path, File.basename(db_path(tenant)) + ".#{timestamp}")
|
|
||||||
FileUtils.cp(backup_file, swept_file)
|
|
||||||
log(tenant) { "Swept backup to #{swept_file.inspect}." }
|
|
||||||
else
|
|
||||||
log(tenant, :warn) { "No backup file found at #{backup_file.inspect} to sweep." }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def enforce_retention(tenant, sweep_dir:, sweep_retention:)
|
|
||||||
unless File.directory?(sweep_dir) && File.writable?(sweep_dir)
|
|
||||||
log(tenant, :warn) { "Skipping retention enforcement, #{sweep_dir.inspect} does not exist or is not writable." }
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
sweep_path = File.join(sweep_dir, Rails.env, tenant)
|
|
||||||
if File.directory?(sweep_path)
|
|
||||||
cutoff_time = Time.now - sweep_retention
|
|
||||||
Find.find(sweep_path) do |path|
|
|
||||||
if File.file?(path) && File.ctime(path) < cutoff_time
|
|
||||||
FileUtils.rm(path)
|
|
||||||
log(tenant) { "Removed old swept backup #{path.inspect}." }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def backup_path(tenant, index)
|
|
||||||
db_path(tenant) + ".#{index}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def db_path(tenant)
|
|
||||||
db_config.config_adapter.database_path
|
|
||||||
end
|
|
||||||
|
|
||||||
def db_config
|
|
||||||
ApplicationRecord.connection_pool.db_config
|
|
||||||
end
|
|
||||||
|
|
||||||
def log(tenant, level = :info, &block)
|
|
||||||
message = block.call
|
|
||||||
Rails.logger.send(level, "[tenant=#{tenant}] SQLiteBackupsJob: #{message}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
class Webhook::CleanupDeliveriesJob < ApplicationJob
|
class Webhook::CleanupDeliveriesJob < ApplicationJob
|
||||||
def perform
|
def perform
|
||||||
ApplicationRecord.with_each_tenant do
|
Webhook::Delivery.cleanup
|
||||||
Webhook::Delivery.cleanup
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
class ApplicationRecord < ActiveRecord::Base
|
class ApplicationRecord < ActiveRecord::Base
|
||||||
primary_abstract_class
|
primary_abstract_class
|
||||||
tenanted
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
bin/rails runner - <<EOF
|
bin/rails runner - <<EOF
|
||||||
puts "Login with david@37signals.com / secret123456 to:"
|
puts "Login with david@37signals.com to:"
|
||||||
ApplicationRecord.with_each_tenant do |tenant|
|
|
||||||
next unless tenant =~ /\A\d+\z/
|
|
||||||
next unless Account.count > 0
|
|
||||||
puts " - #{Account.sole.name}: http://fizzy.localhost:3006#{Account.sole.slug}"
|
|
||||||
end
|
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
if [ -f tmp/solid-queue.txt ]; then
|
if [ -f tmp/solid-queue.txt ]; then
|
||||||
|
|||||||
+13
-35
@@ -1,36 +1,25 @@
|
|||||||
# SQLite. Versions 3.8.0 and up are supported.
|
|
||||||
# gem install sqlite3
|
|
||||||
#
|
|
||||||
# Ensure the SQLite 3 gem is defined in your Gemfile
|
|
||||||
# gem "sqlite3"
|
|
||||||
#
|
|
||||||
default: &default
|
default: &default
|
||||||
adapter: sqlite3
|
adapter: trilogy
|
||||||
|
host: localhost:33384
|
||||||
|
user: root
|
||||||
pool: 50
|
pool: 50
|
||||||
timeout: 5000
|
timeout: 5000
|
||||||
|
|
||||||
development:
|
development:
|
||||||
primary:
|
primary:
|
||||||
<<: *default
|
<<: *default
|
||||||
adapter: sqlite3
|
database: fizzy_development
|
||||||
database: storage/tenants/<%= Rails.env %>/%{tenant}/db/main.sqlite3
|
|
||||||
tenanted: true
|
|
||||||
untenanted:
|
|
||||||
<<: *default
|
|
||||||
adapter: sqlite3
|
|
||||||
database: storage/untenanted/development.sqlite3
|
|
||||||
migrations_paths: db/untenanted_migrate
|
|
||||||
cable:
|
cable:
|
||||||
<<: *default
|
<<: *default
|
||||||
database: storage/development_cable.sqlite3
|
database: fizzy_development_cable
|
||||||
migrations_paths: db/cable_migrate
|
migrations_paths: db/cable_migrate
|
||||||
cache:
|
cache:
|
||||||
<<: *default
|
<<: *default
|
||||||
database: storage/development_cache.sqlite3
|
database: fizzy_development_cache
|
||||||
migrations_paths: db/cache_migrate
|
migrations_paths: db/cache_migrate
|
||||||
queue:
|
queue:
|
||||||
<<: *default
|
<<: *default
|
||||||
database: storage/development_queue.sqlite3
|
database: fizzy_development_queue
|
||||||
migrations_paths: db/queue_migrate
|
migrations_paths: db/queue_migrate
|
||||||
|
|
||||||
# Warning: The database defined as "test" will be erased and
|
# Warning: The database defined as "test" will be erased and
|
||||||
@@ -39,35 +28,24 @@ development:
|
|||||||
test:
|
test:
|
||||||
primary:
|
primary:
|
||||||
<<: *default
|
<<: *default
|
||||||
database: storage/tenants/<%= Rails.env %>/%{tenant}/db/main.sqlite3
|
database: fizzy_test
|
||||||
tenanted: true
|
|
||||||
untenanted:
|
|
||||||
<<: *default
|
|
||||||
database: storage/untenanted/test.sqlite3
|
|
||||||
migrations_paths: db/untenanted_migrate
|
|
||||||
|
|
||||||
production: &production
|
production: &production
|
||||||
primary:
|
primary:
|
||||||
<<: *default
|
<<: *default
|
||||||
adapter: sqlite3
|
adapter: beamer
|
||||||
database: storage/tenants/<%= Rails.env %>/%{tenant}/db/main.sqlite3
|
database: fizzy_<%= Rails.env %>
|
||||||
tenanted: true
|
|
||||||
untenanted:
|
|
||||||
<<: *default
|
|
||||||
adapter: sqlite3
|
|
||||||
database: storage/untenanted/<%= Rails.env %>.sqlite3
|
|
||||||
migrations_paths: db/untenanted_migrate
|
|
||||||
cable:
|
cable:
|
||||||
<<: *default
|
<<: *default
|
||||||
database: storage/production_cable.sqlite3
|
database: fizzy_<% Rails.env %>_cable.sqlite3
|
||||||
migrations_paths: db/cable_migrate
|
migrations_paths: db/cable_migrate
|
||||||
cache:
|
cache:
|
||||||
<<: *default
|
<<: *default
|
||||||
database: storage/production_cache.sqlite3
|
database: fizzy_<% Rails.env %>_cache.sqlite3
|
||||||
migrations_paths: db/cache_migrate
|
migrations_paths: db/cache_migrate
|
||||||
queue:
|
queue:
|
||||||
<<: *default
|
<<: *default
|
||||||
database: storage/production_queue.sqlite3
|
database: fizzy_<% Rails.env %>_queue.sqlite3
|
||||||
migrations_paths: db/queue_migrate
|
migrations_paths: db/queue_migrate
|
||||||
|
|
||||||
beta: *production
|
beta: *production
|
||||||
|
|||||||
@@ -15,31 +15,11 @@ module AccountSlug
|
|||||||
request.engine_script_name = request.script_name = $1
|
request.engine_script_name = request.script_name = $1
|
||||||
request.path_info = $'.empty? ? "/" : $'
|
request.path_info = $'.empty? ? "/" : $'
|
||||||
|
|
||||||
# Return the account id for tenanting.
|
# Return the account id
|
||||||
AccountSlug.decode($2)
|
AccountSlug.decode($2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.decode(slug) slug.to_i end
|
def self.decode(slug) slug.to_i end
|
||||||
def self.encode(id) FORMAT % id end
|
def self.encode(id) FORMAT % id end
|
||||||
|
|
||||||
def self.creation_request?(request)
|
|
||||||
if defined?(Fizzy::Saas) && request.post?
|
|
||||||
path = Fizzy::Saas::Engine.routes.url_helpers.signup_completion_path
|
|
||||||
request.path_info =~ /\A\/#{PATTERN}#{Regexp.escape(path)}\Z/
|
|
||||||
else
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Rails.application.config.after_initialize do
|
|
||||||
Rails.application.config.active_record_tenanted.tenant_resolver = ->(request) do
|
|
||||||
if AccountSlug.creation_request?(request)
|
|
||||||
AccountSlug.extract(request)
|
|
||||||
nil
|
|
||||||
else
|
|
||||||
AccountSlug.extract(request)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
module ActiveStorageControllerExtensions
|
|
||||||
extend ActiveSupport::Concern
|
|
||||||
|
|
||||||
included do
|
|
||||||
before_action do
|
|
||||||
# Add script_name so that Disk Service will generate correct URLs for uploads
|
|
||||||
ActiveStorage::Current.url_options = {
|
|
||||||
protocol: request.protocol,
|
|
||||||
host: request.host,
|
|
||||||
port: request.port,
|
|
||||||
script_name: request.script_name
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Rails.application.config.to_prepare do
|
|
||||||
ActiveStorage::BaseController.include ActiveStorageControllerExtensions
|
|
||||||
end
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
Rails.application.configure do
|
|
||||||
# In test environment, default tenant is set in test_helper.rb
|
|
||||||
if Rails.env.development?
|
|
||||||
config.active_record_tenanted.default_tenant = "686465299" # Honcho
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
ActiveSupport.on_load(:action_controller_base) do
|
# # TODO:PLANB: we should log account id
|
||||||
before_action { logger.struct tenant: ApplicationRecord.current_tenant }
|
# ActiveSupport.on_load(:action_controller_base) do
|
||||||
end
|
# before_action { logger.struct tenant: ApplicationRecord.current_tenant }
|
||||||
|
# end
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
module TurboStreamsJobExtensions
|
# # TODO:PLANB: we probably still need to set script_name properly
|
||||||
extend ActiveSupport::Concern
|
# module TurboStreamsJobExtensions
|
||||||
|
# extend ActiveSupport::Concern
|
||||||
|
|
||||||
class_methods do
|
# class_methods do
|
||||||
def render_format(format, **rendering)
|
# def render_format(format, **rendering)
|
||||||
if ApplicationRecord.current_tenant
|
# if ApplicationRecord.current_tenant
|
||||||
script_name = "/#{ApplicationRecord.current_tenant}"
|
# script_name = "/#{ApplicationRecord.current_tenant}"
|
||||||
ApplicationController.renderer.new(script_name: script_name).render(formats: [ format ], **rendering)
|
# ApplicationController.renderer.new(script_name: script_name).render(formats: [ format ], **rendering)
|
||||||
else
|
# else
|
||||||
super
|
# super
|
||||||
end
|
# end
|
||||||
end
|
# end
|
||||||
end
|
# end
|
||||||
end
|
# end
|
||||||
|
|
||||||
Rails.application.config.after_initialize do
|
# Rails.application.config.after_initialize do
|
||||||
Turbo::StreamsChannel.prepend TurboStreamsJobExtensions
|
# Turbo::StreamsChannel.prepend TurboStreamsJobExtensions
|
||||||
end
|
# end
|
||||||
|
|||||||
@@ -25,9 +25,6 @@ production: &production
|
|||||||
cleanup_magic_links:
|
cleanup_magic_links:
|
||||||
command: "MagicLink.cleanup"
|
command: "MagicLink.cleanup"
|
||||||
schedule: every 4 hours
|
schedule: every 4 hours
|
||||||
sqlite_backups:
|
|
||||||
class: SQLiteBackupsJob
|
|
||||||
schedule: every day at 05:02
|
|
||||||
|
|
||||||
# Metrics
|
# Metrics
|
||||||
yabeda_actioncable:
|
yabeda_actioncable:
|
||||||
|
|||||||
Reference in New Issue
Block a user