From d39635d6cf4ace361a8209e9f004f4ae8988c99a Mon Sep 17 00:00:00 2001 From: Sean Mitchell Date: Thu, 13 Nov 2025 20:11:32 -0800 Subject: [PATCH 001/207] Add logotype to nav --- app/assets/stylesheets/nav.css | 10 +++++++++- app/views/my/_menu.html.erb | 4 ++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/nav.css b/app/assets/stylesheets/nav.css index fda877c20..3a8836e84 100644 --- a/app/assets/stylesheets/nav.css +++ b/app/assets/stylesheets/nav.css @@ -42,11 +42,19 @@ display: grid; height: 1.5em; inline-size: 1.5em; - margin-inline-end: 0.8ch; padding: 0.325em 0.275em 0.225em 0.275em; place-content: center; width: 1.5em; } + + svg { + height: 100%; + margin-inline-start: 0.4125em; + margin-inline-end: 0.5375em; + max-height: 0.8625em; + overflow: visible; + width: auto; + } } /* Dialog diff --git a/app/views/my/_menu.html.erb b/app/views/my/_menu.html.erb index 48570e1ff..2da6d256f 100644 --- a/app/views/my/_menu.html.erb +++ b/app/views/my/_menu.html.erb @@ -3,8 +3,8 @@ action: "click->dialog#open:stop keydown.j@document->hotkey#click keydown.meta+j@document->hotkey#click keydown.ctrl+j@document->hotkey#click", controller: "hotkey" } do %> <%= image_tag "logo.png" %> - Fizzy - J + + J <% end %> <%= tag.dialog class: "nav__menu filter popup popup--animated panel margin-block-start-half", data: { From e5bbe6cf64290e9075330a18193dbcd4d7f5e738 Mon Sep 17 00:00:00 2001 From: Kevin McConnell Date: Mon, 10 Nov 2025 12:04:13 +0000 Subject: [PATCH 002/207] Remove Beamer --- Gemfile | 1 - Gemfile.lock | 11 ----------- bin/beamer | 16 ---------------- config/database.yml | 8 ++++---- config/deploy.beta.yml | 23 ----------------------- config/deploy.production.yml | 27 --------------------------- config/deploy.staging.yml | 27 --------------------------- config/deploy.yml | 1 - 8 files changed, 4 insertions(+), 110 deletions(-) delete mode 100755 bin/beamer diff --git a/Gemfile b/Gemfile index 17e33abd2..cc719e2d8 100644 --- a/Gemfile +++ b/Gemfile @@ -19,7 +19,6 @@ gem "solid_cache", "~> 1.0" gem "solid_queue", "~> 1.2" gem "sqlite3", ">= 2.0" gem "thruster", require: false -gem "beamer-rails", "~> 0.1.0.beta4" # Features gem "bcrypt", "~> 3.1.7" diff --git a/Gemfile.lock b/Gemfile.lock index 16162cb8b..802fe5e78 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -184,16 +184,6 @@ GEM base64 (0.3.0) bcrypt (3.1.20) bcrypt_pbkdf (1.1.1) - beamer-rails (0.1.0.beta4-arm64-darwin) - sqlite3 (>= 2.0) - beamer-rails (0.1.0.beta4-arm64-linux-gnu) - sqlite3 (>= 2.0) - beamer-rails (0.1.0.beta4-x86_64-darwin) - sqlite3 (>= 2.0) - beamer-rails (0.1.0.beta4-x86_64-linux-gnu) - sqlite3 (>= 2.0) - beamer-rails (0.1.0.beta4-x86_64-linux-musl) - sqlite3 (>= 2.0) benchmark (0.5.0) bigdecimal (3.3.1) bindex (0.8.1) @@ -600,7 +590,6 @@ DEPENDENCIES autotuner aws-sdk-s3 bcrypt (~> 3.1.7) - beamer-rails (~> 0.1.0.beta4) benchmark bootsnap brakeman diff --git a/bin/beamer b/bin/beamer deleted file mode 100755 index b0b4f14e4..000000000 --- a/bin/beamer +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -# -# This file was generated by Bundler. -# -# The application 'beamer' is installed as part of a gem, and -# this file is here to facilitate running it. -# - -ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) - -require "rubygems" -require "bundler/setup" - -load Gem.bin_path("beamer-rails", "beamer") diff --git a/config/database.yml b/config/database.yml index de3b50a9b..a1111d8f2 100644 --- a/config/database.yml +++ b/config/database.yml @@ -12,12 +12,12 @@ default: &default development: primary: <<: *default - adapter: <%= %w[1 true].include?(ENV["BEAMER"]&.downcase) ? "beamer" : "sqlite3" %> + adapter: sqlite3 database: storage/tenants/<%= Rails.env %>/%{tenant}/db/main.sqlite3 tenanted: true untenanted: <<: *default - adapter: <%= %w[1 true].include?(ENV["BEAMER"]&.downcase) ? "beamer" : "sqlite3" %> + adapter: sqlite3 database: storage/untenanted/development.sqlite3 migrations_paths: db/untenanted_migrate cable: @@ -49,12 +49,12 @@ test: production: &production primary: <<: *default - adapter: beamer + adapter: sqlite3 database: storage/tenants/<%= Rails.env %>/%{tenant}/db/main.sqlite3 tenanted: true untenanted: <<: *default - adapter: beamer + adapter: sqlite3 database: storage/untenanted/<%= Rails.env %>.sqlite3 migrations_paths: db/untenanted_migrate cable: diff --git a/config/deploy.beta.yml b/config/deploy.beta.yml index 59956263e..f1db401c7 100644 --- a/config/deploy.beta.yml +++ b/config/deploy.beta.yml @@ -22,29 +22,6 @@ env: RAILS_ENV: beta accessories: - beamer: - image: basecamp/beamer:zones - registry: - server: registry.37signals.com - username: robot$harbor-bot - password: - - BASECAMP_REGISTRY_PASSWORD - labels: - otel_role: beamer - otel_service: fizzy-beamer - otel_scrape_enabled: true - options: - user: 1000 # Match the UID of the Rails app, for volume permissions - publish: - - 5001:5001 - - 9000:9000 - volumes: - - fizzy:/home/beamer - cmd: beamer run --retention=1h --metrics-port=9000 - hosts: - - fizzy-beta-app-01.sc-chi-int.37signals.com - - fizzy-beta-app-101.df-iad-int.37signals.com - load-balancer: image: basecamp/kamal-proxy:lb host: fizzy-beta-lb-01.sc-chi-int.37signals.com diff --git a/config/deploy.production.yml b/config/deploy.production.yml index b2e7b2bbb..6ae1172ce 100644 --- a/config/deploy.production.yml +++ b/config/deploy.production.yml @@ -30,33 +30,6 @@ env: RAILS_ENV: production accessories: - beamer: - image: basecamp/beamer:zones - registry: - server: registry.37signals.com - username: robot$harbor-bot - password: - - BASECAMP_REGISTRY_PASSWORD - labels: - otel_role: beamer - otel_service: fizzy-beamer - otel_scrape_enabled: true - options: - user: 1000 # Match the UID of the Rails app, for volume permissions - publish: - - 5001:5001 - - 9000:9000 - volumes: - - fizzy:/home/beamer - cmd: beamer run --retention=1h --metrics-port=9000 - hosts: - - fizzy-app-101.df-iad-int.37signals.com - - fizzy-app-102.df-iad-int.37signals.com - - fizzy-app-01.sc-chi-int.37signals.com - - fizzy-app-02.sc-chi-int.37signals.com - - fizzy-app-401.df-ams-int.37signals.com - - fizzy-app-402.df-ams-int.37signals.com - load-balancer: image: basecamp/kamal-proxy:lb hosts: diff --git a/config/deploy.staging.yml b/config/deploy.staging.yml index e0cb7fbce..8ee71d6fa 100644 --- a/config/deploy.staging.yml +++ b/config/deploy.staging.yml @@ -26,33 +26,6 @@ env: RAILS_ENV: staging accessories: - beamer: - image: basecamp/beamer:zones - registry: - server: registry.37signals.com - username: robot$harbor-bot - password: - - BASECAMP_REGISTRY_PASSWORD - labels: - otel_role: beamer - otel_service: fizzy-beamer - otel_scrape_enabled: true - options: - user: 1000 # Match the UID of the Rails app, for volume permissions - publish: - - 5001:5001 - - 9000:9000 - volumes: - - fizzy:/home/beamer - cmd: beamer run --retention=1h --metrics-port=9000 - hosts: - - fizzy-staging-app-01.sc-chi-int.37signals.com - - fizzy-staging-app-02.sc-chi-int.37signals.com - - fizzy-staging-app-101.df-iad-int.37signals.com - - fizzy-staging-app-102.df-iad-int.37signals.com - - fizzy-staging-app-401.df-ams-int.37signals.com - - fizzy-staging-app-402.df-ams-int.37signals.com - load-balancer: image: basecamp/kamal-proxy:lb hosts: diff --git a/config/deploy.yml b/config/deploy.yml index 8fa2f5b4c..b565e5a50 100644 --- a/config/deploy.yml +++ b/config/deploy.yml @@ -32,4 +32,3 @@ env: aliases: console: app exec -i --reuse "bin/rails console" ssh: app exec -i --reuse /bin/bash - switch: accessory exec beamer --reuse "beamer switch --primary=<%= ENV["PRIMARY"] %>" From f66544b97a56afdbcc64d905a1cadb05dabf6128 Mon Sep 17 00:00:00 2001 From: Kevin McConnell Date: Mon, 10 Nov 2025 12:08:46 +0000 Subject: [PATCH 003/207] Remove testbed --- Procfile.beamer-testbed | 3 - bin/beamer-testbed | 235 ------------------------------ docker-compose.beamer-testbed.yml | 55 ------- 3 files changed, 293 deletions(-) delete mode 100644 Procfile.beamer-testbed delete mode 100755 bin/beamer-testbed delete mode 100644 docker-compose.beamer-testbed.yml diff --git a/Procfile.beamer-testbed b/Procfile.beamer-testbed deleted file mode 100644 index 05fe908af..000000000 --- a/Procfile.beamer-testbed +++ /dev/null @@ -1,3 +0,0 @@ -web: bin/thrust bin/rails server -beamer: bin/beamer --debug --directory ./storage/ run --primary ${BEAMER_PRIMARY:-$(hostname)} -migrations: bin/beamer-testbed run-migrations --debug --directory ./storage/ diff --git a/bin/beamer-testbed b/bin/beamer-testbed deleted file mode 100755 index baf667501..000000000 --- a/bin/beamer-testbed +++ /dev/null @@ -1,235 +0,0 @@ -#!/usr/bin/env bash - -set -e - -COMPOSE_OPTIONS="-f docker-compose.beamer-testbed.yml" - -# Show usage -show_usage() { - echo "Usage: $0 {start|stop|logs|console|bash|server|run-migrations} [options]" - echo "" - echo "Commands:" - echo " start Build and start all services" - echo " stop Stop all services" - echo " logs Show logs (pass -f to follow)" - echo " console [node] Open Rails console (default: fizzy-01)" - echo " bash [node] Open bash shell (default: fizzy-01)" - echo " server Start web server and beamer using foreman" - echo " run-migrations Wait for beamer, then run migrations if primary" - exit 1 -} - -# Get all fizzy-* services from docker compose -get_fizzy_services() { - fizzy_services=$(docker compose $COMPOSE_OPTIONS config --services | grep '^fizzy-' | sort) - - if [ -z "$fizzy_services" ]; then - echo "Error: No fizzy-* services found in docker-compose file" - exit 1 - fi - - echo "$fizzy_services" -} - -# Function to wait for a container to be healthy -wait_for_container() { - local container=$1 - local max_attempts=60 - local attempt=0 - - echo "Waiting for $container to be ready..." - - while [ $attempt -lt $max_attempts ]; do - if docker compose $COMPOSE_OPTIONS exec -T "$container" curl -f -s http://localhost:3000/up >/dev/null 2>&1; then - echo "$container is ready!" - return 0 - fi - attempt=$((attempt + 1)) - echo "$container isn't yet ready on attempt $attempt/$max_attempts" - sleep 2 - done - - echo "Error: $container failed to become ready after $max_attempts attempts" - return 1 -} - -# Function to configure kamal-proxy -# TODO: set up the other containers as read targets -configure_proxy() { - local writer=$1 - local readers=${@:2} - - echo "Configuring kamal-proxy..." - - local writer_target="$writer:3000" - local reader_target="" - for reader in $readers; do - if [ -n "$reader_target" ]; then - reader_target+="," - fi - reader_target+="$reader:3000" - done - - docker compose $COMPOSE_OPTIONS exec -T kamal-proxy \ - kamal-proxy deploy fizzy \ - --target $writer_target \ - --read-target $reader_target \ - --host fizzy.localhost - - echo "Configured kamal-proxy with target=${writer_target} and read-target=${reader_target}" -} - -# Start command -cmd_start() { - local fizzy_services=$(get_fizzy_services) - local first_fizzy=$(echo "$fizzy_services" | head -n 1) - - # Set UID and GID for Docker user mapping - export DOCKER_UID=$(id -u) - export DOCKER_GID=$(id -g) - - # Get GitHub token from gh CLI - echo "Getting GitHub token from gh CLI..." - export GITHUB_TOKEN=$(gh auth token) - - if [ -z "$GITHUB_TOKEN" ]; then - echo "Error: Failed to get GitHub token from gh CLI" - echo "Make sure you're authenticated with: gh auth login" - exit 1 - fi - - # Create tmp directories with proper permissions - echo "Creating temporary directories..." - for service in $fizzy_services; do - mkdir -p "./tmp/beamer-testbed/$service/storage" - mkdir -p "./tmp/beamer-testbed/$service/tmp" - done - - echo "Building Docker image for $first_fizzy..." - docker compose $COMPOSE_OPTIONS build "$first_fizzy" - - echo "Starting services in detached mode..." - docker compose $COMPOSE_OPTIONS down - docker compose $COMPOSE_OPTIONS up -d - - # Wait for and register each fizzy container - for container in $fizzy_services; do - wait_for_container "$container" - done - configure_proxy $fizzy_services - - echo "" - echo "All containers are ready and registered!" - echo "You can access the application at: http://fizzy.localhost:3006" -} - -# Stop command -cmd_stop() { - echo "Stopping services..." - docker compose $COMPOSE_OPTIONS down - echo "Services stopped." -} - -# Logs command -cmd_logs() { - docker compose $COMPOSE_OPTIONS logs "$@" -} - -# Console command -cmd_console() { - local node="${1:-fizzy-01}" - - echo "Opening Rails console in $node..." - docker compose $COMPOSE_OPTIONS exec "$node" bin/rails console -} - -# Bash command -cmd_bash() { - local node="${1:-fizzy-01}" - - echo "Opening bash shell in $node..." - docker compose $COMPOSE_OPTIONS exec "$node" bash -} - -# Server command (runs inside container) -cmd_server() { - # Check if foreman is installed - if ! command -v foreman >/dev/null 2>&1; then - echo "Foreman not found, installing..." - gem install foreman - fi - - # Clean up any stale .beamer.sock files - echo "Cleaning up stale socket files..." - find . -path '*/.beamer/*/.beamer.sock' -type s -delete 2>/dev/null || true - - mkdir -p ./storage/tenants - mkdir -p ./storage/untenanted - - echo "Starting web server and beamer with foreman..." - exec foreman start -f Procfile.beamer-testbed -} - -# Run migrations command (runs inside container) -cmd_run_migrations() { - # Store all arguments as BEAMER_OPTIONS - BEAMER_OPTIONS="$*" - - echo "Waiting for beamer to be ready..." - - # Wait for beamer status to succeed - while ! bin/beamer $BEAMER_OPTIONS status >/dev/null 2>&1; do - echo "Beamer not ready yet, waiting..." - sleep 1 - done - - echo "Beamer is ready!" - sleep 1 - - # Check if this is the primary node - if bin/beamer $BEAMER_OPTIONS is-primary; then - echo "This is the primary node, running migrations..." - ./bin/rails db:prepare - echo "Migrations complete!" - else - echo "This is not the primary node, skipping migrations." - fi - - # Wait indefinitely until interrupted - echo "Waiting indefinitely (press Ctrl-C to exit)..." - while true; do - sleep 3600 - done -} - -# Main command dispatch -case "${1:-}" in -start) - cmd_start - ;; -stop) - cmd_stop - ;; -logs) - shift - cmd_logs "$@" - ;; -console) - shift - cmd_console "$@" - ;; -bash) - shift - cmd_bash "$@" - ;; -server) - cmd_server - ;; -run-migrations) - shift - cmd_run_migrations "$@" - ;; -*) - show_usage - ;; -esac diff --git a/docker-compose.beamer-testbed.yml b/docker-compose.beamer-testbed.yml deleted file mode 100644 index 0cbbffab2..000000000 --- a/docker-compose.beamer-testbed.yml +++ /dev/null @@ -1,55 +0,0 @@ -x-app: &app - image: "basecamp/fizzy:dev" - user: "${DOCKER_UID:-1000}:${DOCKER_GID:-1000}" - build: - context: . - dockerfile: Dockerfile.dev - secrets: - - GITHUB_TOKEN - volumes: - - .:/rails - extra_hosts: - - queenbee.localhost:host-gateway - environment: - - RAILS_ENV=development - - BINDING=0.0.0.0 - - BEAMER=true - - BEAMER_PRIMARY=fizzy-01 - - SECRET_KEY_BASE=beamer_testbed_shared_secret_key_base_for_development_only - command: [ "./bin/beamer-testbed", "server" ] - -services: - kamal-proxy: - image: basecamp/kamal-proxy:next - ports: - - 127.0.0.1:3006:80 - - 127.0.0.1:443:443 - command: [ "kamal-proxy", "run", "--debug" ] - - fizzy-01: - <<: *app - hostname: fizzy-01 - volumes: - - .:/rails - - ./tmp/beamer-testbed/fizzy-01/storage:/rails/storage - - ./tmp/beamer-testbed/fizzy-01/tmp:/rails/tmp - - fizzy-02: - <<: *app - hostname: fizzy-02 - volumes: - - .:/rails - - ./tmp/beamer-testbed/fizzy-02/storage:/rails/storage - - ./tmp/beamer-testbed/fizzy-02/tmp:/rails/tmp - - fizzy-03: - <<: *app - hostname: fizzy-03 - volumes: - - .:/rails - - ./tmp/beamer-testbed/fizzy-03/storage:/rails/storage - - ./tmp/beamer-testbed/fizzy-03/tmp:/rails/tmp - -secrets: - GITHUB_TOKEN: - environment: GITHUB_TOKEN From ea64ba6f9e8b56c6732efb0ea20aa6d845be9b1a Mon Sep 17 00:00:00 2001 From: Kevin McConnell Date: Mon, 10 Nov 2025 12:13:36 +0000 Subject: [PATCH 004/207] Use regular db:prepare now --- bin/docker-entrypoint | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/docker-entrypoint b/bin/docker-entrypoint index f3c243ae0..4f8ccb547 100755 --- a/bin/docker-entrypoint +++ b/bin/docker-entrypoint @@ -2,7 +2,7 @@ # If running the rails server then create or migrate existing database if [ "${1}" == "./bin/thrust" ] && [ "${2}" == "./bin/rails" ] && [ "${3}" == "server" ]; then - ./bin/migrate-primary-db + ./bin/rails db:prepare fi exec "${@}" From dc40d2b5b062849da2cd1956069953a193054c1c Mon Sep 17 00:00:00 2001 From: Kevin McConnell Date: Mon, 10 Nov 2025 12:18:20 +0000 Subject: [PATCH 005/207] Remove custom read/writer routing --- app/controllers/application_controller.rb | 2 +- .../concerns/load_balancer_routing.rb | 39 ------------------- 2 files changed, 1 insertion(+), 40 deletions(-) delete mode 100644 app/controllers/concerns/load_balancer_routing.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 235b9c2a0..f27627150 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,10 +1,10 @@ class ApplicationController < ActionController::Base - include LoadBalancerRouting, WriterAffinity include Authentication include Authorization include CurrentRequest, CurrentTimezone, SetPlatform include TurboFlash, ViewTransitions include Saas + include WriterAffinity stale_when_importmap_changes allow_browser versions: :modern diff --git a/app/controllers/concerns/load_balancer_routing.rb b/app/controllers/concerns/load_balancer_routing.rb deleted file mode 100644 index 5ddc1cfa9..000000000 --- a/app/controllers/concerns/load_balancer_routing.rb +++ /dev/null @@ -1,39 +0,0 @@ -module LoadBalancerRouting - extend ActiveSupport::Concern - - ALLOWED_PRAGMAS = %w[ beamer_primary beamer_last_txn ] - - included do - before_action :set_target_header, :set_writer_header - after_action :set_transaction_cookie - end - - private - def set_target_header - response.headers["X-Kamal-Target"] = request.headers["X-Kamal-Target"] - end - - def set_writer_header - if ApplicationRecord.current_tenant.present? - response.headers["X-Writer"] = pragma("beamer_primary") - end - end - - def set_transaction_cookie - unless safe_request? - if ApplicationRecord.current_tenant.present? && Account.sole.present? - cookies[:last_transaction] = { value: pragma("beamer_last_txn"), path: Account.sole.slug } - end - end - end - - def pragma(name) - if ALLOWED_PRAGMAS.include?(name) - ApplicationRecord.connection.execute("pragma #{name}").first&.values&.first - end - end - - def safe_request? - request.get? || request.head? - end -end From 9fa68c0ea938bd6302bc6c81f870955e5941ae70 Mon Sep 17 00:00:00 2001 From: Kevin McConnell Date: Mon, 10 Nov 2025 12:45:49 +0000 Subject: [PATCH 006/207] Add Trilogy adapter --- Gemfile | 1 + Gemfile.lock | 2 ++ bin/setup | 21 ++++++++++++--------- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/Gemfile b/Gemfile index cc719e2d8..5932188ce 100644 --- a/Gemfile +++ b/Gemfile @@ -19,6 +19,7 @@ gem "solid_cache", "~> 1.0" gem "solid_queue", "~> 1.2" gem "sqlite3", ">= 2.0" gem "thruster", require: false +gem "trilogy", "~> 2.9" # Features gem "bcrypt", "~> 3.1.7" diff --git a/Gemfile.lock b/Gemfile.lock index 802fe5e78..c16b85b63 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -514,6 +514,7 @@ GEM thruster (0.1.16-x86_64-darwin) thruster (0.1.16-x86_64-linux) timeout (0.4.4) + trilogy (2.9.0) tsort (0.2.0) turbo-rails (2.0.20) actionpack (>= 7.1.0) @@ -628,6 +629,7 @@ DEPENDENCIES sqlite3 (>= 2.0) stimulus-rails thruster + trilogy (~> 2.9) turbo-rails vcr web-console diff --git a/bin/setup b/bin/setup index b32c2d96c..414077301 100755 --- a/bin/setup +++ b/bin/setup @@ -2,16 +2,19 @@ set -eo pipefail # Prefer app executables -app_root="$(cd "$(dirname "$0")/.."; pwd)" +app_root="$( + cd "$(dirname "$0")/.." + pwd +)" export PATH="$app_root/bin:$PATH" # Install gum if needed -if ! command -v gum &> /dev/null; then +if ! command -v gum &>/dev/null; then echo echo "▸ Installing gum" - if command -v pacman &> /dev/null; then + if command -v pacman &>/dev/null; then sudo pacman -S --noconfirm gum - elif command -v brew &> /dev/null; then + elif command -v brew &>/dev/null; then brew install gum else echo "Please install gum: https://github.com/charmbracelet/gum" @@ -35,12 +38,12 @@ step() { } needs_seeding() { - if [ "$CI" != "" ] ; then + if [ "$CI" != "" ]; then return 1 fi has_tenant=$(bin/rails runner "pp ApplicationRecord.tenants.any? { ApplicationRecord.with_tenant(_1) { Account.sole } }" 2>/dev/null) - if [ "$has_tenant" = "true" ] ; then + if [ "$has_tenant" = "true" ]; then return 1 else return 0 @@ -48,7 +51,7 @@ needs_seeding() { } echo -gum style --foreground 153 " ˚ ∘ ∘ ˚ " +gum style --foreground 153 " ˚ ∘ ∘ ˚ " gum style --foreground 111 --bold " ∘˚˳°∘° 𝒻𝒾𝓏𝓏𝓎 °∘°˳˚∘ " echo @@ -56,7 +59,7 @@ step "Installing Ruby" mise install --yes eval "$(mise hook-env)" if which pacman >/dev/null 2>&1; then - packages=(imagemagick openslide libvips) + packages=(imagemagick mariadb-libs openslide libvips) if ! pacman -Q "${packages[@]}" >/dev/null 2>&1; then step "Installing packages" sudo pacman -S --noconfirm --needed "${packages[@]}" fi @@ -68,7 +71,7 @@ step "Set up gh-signoff" bash -c "gh extension install basecamp/gh-signoff || gh bundle config set --local auto_install true step "Installing RubyGems" bundle install -if [ -e tmp/minio-dev.txt ] ; then +if [ -e tmp/minio-dev.txt ]; then step "Starting Docker services" bash -c "[ -d ~/Work/basecamp/docker-dev ] && git -C ~/Work/basecamp/docker-dev pull || gh repo clone basecamp/docker-dev ~/Work/basecamp/docker-dev && ~/Work/basecamp/docker-dev/setup minio" step "Configuring MinIO" bin/minio-setup From 4d3c26504262337fc25d79e72de32950c552bef2 Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Mon, 10 Nov 2025 07:12:59 -0500 Subject: [PATCH 007/207] Rip out activerecord-tenanting Co-authored-by: Stanko K.R. --- Gemfile | 1 - Gemfile.lock | 50 +++--- app/jobs/card/auto_postpone_all_due_job.rb | 4 +- app/jobs/delete_unused_tags_job.rb | 6 +- .../notification/bundle/deliver_all_job.rb | 4 +- app/jobs/sqlite_backups_job.rb | 155 ------------------ app/jobs/webhook/cleanup_deliveries_job.rb | 4 +- app/models/application_record.rb | 1 - bin/dev | 7 +- config/database.yml | 48 ++---- config/initializers/tenanting/account_slug.rb | 22 +-- .../initializers/tenanting/active_storage.rb | 19 --- .../initializers/tenanting/default_tenant.rb | 6 - config/initializers/tenanting/logging.rb | 7 +- config/initializers/tenanting/turbo.rb | 33 ++-- config/recurring.yml | 3 - 16 files changed, 61 insertions(+), 309 deletions(-) delete mode 100644 app/jobs/sqlite_backups_job.rb delete mode 100644 config/initializers/tenanting/active_storage.rb delete mode 100644 config/initializers/tenanting/default_tenant.rb diff --git a/Gemfile b/Gemfile index 5932188ce..5df3f6d74 100644 --- a/Gemfile +++ b/Gemfile @@ -10,7 +10,6 @@ gem "stimulus-rails" gem "turbo-rails" # Deployment and drivers -gem "activerecord-tenanted", bc: "activerecord-tenanted" gem "bootsnap", require: false gem "kamal", require: false gem "puma", ">= 5.0" diff --git a/Gemfile.lock b/Gemfile.lock index c16b85b63..b0066fd78 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -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 remote: https://github.com/basecamp/queenbee-plugin revision: eb01c697de1ad028afc65cc7d9b5345a7a8e849f @@ -161,10 +152,10 @@ GEM anyway_config (2.7.2) ruby-next-core (~> 1.0) ast (2.4.3) - autotuner (1.1.0) + autotuner (1.0.2) aws-eventstream (1.4.0) - aws-partitions (1.1181.0) - aws-sdk-core (3.236.0) + aws-partitions (1.1175.0) + aws-sdk-core (3.234.0) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.992.0) aws-sigv4 (~> 1.9) @@ -172,10 +163,10 @@ GEM bigdecimal jmespath (~> 1, >= 1.6.1) logger - aws-sdk-kms (1.117.0) + aws-sdk-kms (1.115.0) aws-sdk-core (~> 3, >= 3.234.0) 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-kms (~> 1) aws-sigv4 (~> 1.5) @@ -189,7 +180,7 @@ GEM bindex (0.8.1) bootsnap (1.18.6) msgpack (~> 1.2) - brakeman (7.1.1) + brakeman (7.1.0) racc builder (3.3.0) bundler-audit (0.9.2) @@ -259,7 +250,7 @@ GEM actionview (>= 7.0.0) activesupport (>= 7.0.0) jmespath (1.6.2) - json (2.16.0) + json (2.15.2) jwt (3.1.2) base64 kamal (2.8.2) @@ -299,7 +290,7 @@ GEM logger mini_mime (1.1.5) mini_portile2 (2.8.9) - minitest (5.26.1) + minitest (5.26.0) mission_control-jobs (1.1.0) actioncable (>= 7.1) actionpack (>= 7.1) @@ -417,7 +408,7 @@ GEM tsort redcarpet (3.6.1) regexp_parser (2.11.3) - reline (0.6.3) + reline (0.6.2) io-console (~> 0.5) rexml (3.4.4) rouge (4.6.1) @@ -467,10 +458,10 @@ GEM rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 4.0) websocket (~> 1.0) - sentry-rails (6.1.0) + sentry-rails (6.0.0) railties (>= 5.2.0) - sentry-ruby (~> 6.1.0) - sentry-ruby (6.1.0) + sentry-ruby (~> 6.0.0) + sentry-ruby (6.0.0) bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) sniffer (0.5.0) @@ -481,23 +472,23 @@ GEM activejob (>= 7.2) activerecord (>= 7.2) railties (>= 7.2) - solid_cache (1.0.10) + solid_cache (1.0.8) activejob (>= 7.2) activerecord (>= 7.2) railties (>= 7.2) - solid_queue (1.2.4) + solid_queue (1.2.2) activejob (>= 7.1) activerecord (>= 7.1) concurrent-ruby (>= 1.3.1) fugit (~> 1.11) railties (>= 7.1) thor (>= 1.3.1) - sqlite3 (2.8.0) + sqlite3 (2.7.4) mini_portile2 (~> 2.8.0) - sqlite3 (2.8.0-arm64-darwin) - sqlite3 (2.8.0-x86_64-darwin) - sqlite3 (2.8.0-x86_64-linux-gnu) - sqlite3 (2.8.0-x86_64-linux-musl) + sqlite3 (2.7.4-arm64-darwin) + sqlite3 (2.7.4-x86_64-darwin) + sqlite3 (2.7.4-x86_64-linux-gnu) + sqlite3 (2.7.4-x86_64-linux-musl) sshkit (1.24.0) base64 logger @@ -536,7 +527,7 @@ GEM web-push (3.0.2) jwt (~> 3.0) openssl (~> 3.0) - webmock (3.26.1) + webmock (3.26.0) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) @@ -586,7 +577,6 @@ PLATFORMS x86_64-linux-musl DEPENDENCIES - activerecord-tenanted! activeresource autotuner aws-sdk-s3 diff --git a/app/jobs/card/auto_postpone_all_due_job.rb b/app/jobs/card/auto_postpone_all_due_job.rb index 2b75f0736..684e2cbe2 100644 --- a/app/jobs/card/auto_postpone_all_due_job.rb +++ b/app/jobs/card/auto_postpone_all_due_job.rb @@ -1,7 +1,5 @@ class Card::AutoPostponeAllDueJob < ApplicationJob def perform - ApplicationRecord.with_each_tenant do |tenant| - Card.auto_postpone_all_due - end + Card.auto_postpone_all_due end end diff --git a/app/jobs/delete_unused_tags_job.rb b/app/jobs/delete_unused_tags_job.rb index a733a0e75..375984ba6 100644 --- a/app/jobs/delete_unused_tags_job.rb +++ b/app/jobs/delete_unused_tags_job.rb @@ -1,9 +1,7 @@ class DeleteUnusedTagsJob < ApplicationJob def perform - ApplicationRecord.with_each_tenant do |tenant| - Tag.unused.find_each do |tag| - tag.destroy! - end + Tag.unused.find_each do |tag| + tag.destroy! end end end diff --git a/app/jobs/notification/bundle/deliver_all_job.rb b/app/jobs/notification/bundle/deliver_all_job.rb index 250b53b6e..79aeaed16 100644 --- a/app/jobs/notification/bundle/deliver_all_job.rb +++ b/app/jobs/notification/bundle/deliver_all_job.rb @@ -2,8 +2,6 @@ class Notification::Bundle::DeliverAllJob < ApplicationJob queue_as :backend def perform - ApplicationRecord.with_each_tenant do |tenant| - Notification::Bundle.deliver_all - end + Notification::Bundle.deliver_all end end diff --git a/app/jobs/sqlite_backups_job.rb b/app/jobs/sqlite_backups_job.rb deleted file mode 100644 index 5daea366c..000000000 --- a/app/jobs/sqlite_backups_job.rb +++ /dev/null @@ -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 %.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 diff --git a/app/jobs/webhook/cleanup_deliveries_job.rb b/app/jobs/webhook/cleanup_deliveries_job.rb index 63f87ead5..37439de91 100644 --- a/app/jobs/webhook/cleanup_deliveries_job.rb +++ b/app/jobs/webhook/cleanup_deliveries_job.rb @@ -1,7 +1,5 @@ class Webhook::CleanupDeliveriesJob < ApplicationJob def perform - ApplicationRecord.with_each_tenant do - Webhook::Delivery.cleanup - end + Webhook::Delivery.cleanup end end diff --git a/app/models/application_record.rb b/app/models/application_record.rb index cb75f5d41..b63caeb8a 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -1,4 +1,3 @@ class ApplicationRecord < ActiveRecord::Base primary_abstract_class - tenanted end diff --git a/bin/dev b/bin/dev index 1c3b7427a..a798bc227 100755 --- a/bin/dev +++ b/bin/dev @@ -1,12 +1,7 @@ #!/usr/bin/env sh bin/rails runner - < 0 - puts " - #{Account.sole.name}: http://fizzy.localhost:3006#{Account.sole.slug}" - end + puts "Login with david@37signals.com to:" EOF if [ -f tmp/solid-queue.txt ]; then diff --git a/config/database.yml b/config/database.yml index a1111d8f2..79ae7d99c 100644 --- a/config/database.yml +++ b/config/database.yml @@ -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 - adapter: sqlite3 + adapter: trilogy + host: localhost:33384 + user: root pool: 50 timeout: 5000 development: primary: <<: *default - adapter: sqlite3 - 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 + database: fizzy_development cable: <<: *default - database: storage/development_cable.sqlite3 + database: fizzy_development_cable migrations_paths: db/cable_migrate cache: <<: *default - database: storage/development_cache.sqlite3 + database: fizzy_development_cache migrations_paths: db/cache_migrate queue: <<: *default - database: storage/development_queue.sqlite3 + database: fizzy_development_queue migrations_paths: db/queue_migrate # Warning: The database defined as "test" will be erased and @@ -39,35 +28,24 @@ development: test: primary: <<: *default - database: storage/tenants/<%= Rails.env %>/%{tenant}/db/main.sqlite3 - tenanted: true - untenanted: - <<: *default - database: storage/untenanted/test.sqlite3 - migrations_paths: db/untenanted_migrate + database: fizzy_test production: &production primary: <<: *default - adapter: sqlite3 - database: storage/tenants/<%= Rails.env %>/%{tenant}/db/main.sqlite3 - tenanted: true - untenanted: - <<: *default - adapter: sqlite3 - database: storage/untenanted/<%= Rails.env %>.sqlite3 - migrations_paths: db/untenanted_migrate + adapter: beamer + database: fizzy_<%= Rails.env %> cable: <<: *default - database: storage/production_cable.sqlite3 + database: fizzy_<% Rails.env %>_cable.sqlite3 migrations_paths: db/cable_migrate cache: <<: *default - database: storage/production_cache.sqlite3 + database: fizzy_<% Rails.env %>_cache.sqlite3 migrations_paths: db/cache_migrate queue: <<: *default - database: storage/production_queue.sqlite3 + database: fizzy_<% Rails.env %>_queue.sqlite3 migrations_paths: db/queue_migrate beta: *production diff --git a/config/initializers/tenanting/account_slug.rb b/config/initializers/tenanting/account_slug.rb index b2c0cdb40..6a4f83eb9 100644 --- a/config/initializers/tenanting/account_slug.rb +++ b/config/initializers/tenanting/account_slug.rb @@ -15,31 +15,11 @@ module AccountSlug request.engine_script_name = request.script_name = $1 request.path_info = $'.empty? ? "/" : $' - # Return the account id for tenanting. + # Return the account id AccountSlug.decode($2) end end def self.decode(slug) slug.to_i 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 diff --git a/config/initializers/tenanting/active_storage.rb b/config/initializers/tenanting/active_storage.rb deleted file mode 100644 index f57cdc304..000000000 --- a/config/initializers/tenanting/active_storage.rb +++ /dev/null @@ -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 diff --git a/config/initializers/tenanting/default_tenant.rb b/config/initializers/tenanting/default_tenant.rb deleted file mode 100644 index 2066f61d6..000000000 --- a/config/initializers/tenanting/default_tenant.rb +++ /dev/null @@ -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 diff --git a/config/initializers/tenanting/logging.rb b/config/initializers/tenanting/logging.rb index d6675942e..08733395b 100644 --- a/config/initializers/tenanting/logging.rb +++ b/config/initializers/tenanting/logging.rb @@ -1,3 +1,4 @@ -ActiveSupport.on_load(:action_controller_base) do - before_action { logger.struct tenant: ApplicationRecord.current_tenant } -end +# # TODO:PLANB: we should log account id +# ActiveSupport.on_load(:action_controller_base) do +# before_action { logger.struct tenant: ApplicationRecord.current_tenant } +# end diff --git a/config/initializers/tenanting/turbo.rb b/config/initializers/tenanting/turbo.rb index d740ff8f6..67447a58b 100644 --- a/config/initializers/tenanting/turbo.rb +++ b/config/initializers/tenanting/turbo.rb @@ -1,18 +1,19 @@ -module TurboStreamsJobExtensions - extend ActiveSupport::Concern +# # TODO:PLANB: we probably still need to set script_name properly +# module TurboStreamsJobExtensions +# extend ActiveSupport::Concern - class_methods do - def render_format(format, **rendering) - if ApplicationRecord.current_tenant - script_name = "/#{ApplicationRecord.current_tenant}" - ApplicationController.renderer.new(script_name: script_name).render(formats: [ format ], **rendering) - else - super - end - end - end -end +# class_methods do +# def render_format(format, **rendering) +# if ApplicationRecord.current_tenant +# script_name = "/#{ApplicationRecord.current_tenant}" +# ApplicationController.renderer.new(script_name: script_name).render(formats: [ format ], **rendering) +# else +# super +# end +# end +# end +# end -Rails.application.config.after_initialize do - Turbo::StreamsChannel.prepend TurboStreamsJobExtensions -end +# Rails.application.config.after_initialize do +# Turbo::StreamsChannel.prepend TurboStreamsJobExtensions +# end diff --git a/config/recurring.yml b/config/recurring.yml index 15067065b..eec4772d5 100644 --- a/config/recurring.yml +++ b/config/recurring.yml @@ -25,9 +25,6 @@ production: &production cleanup_magic_links: command: "MagicLink.cleanup" schedule: every 4 hours - sqlite_backups: - class: SQLiteBackupsJob - schedule: every day at 05:02 # Metrics yabeda_actioncable: From 6705b5225f4d31759d84b9b40faa865a7d879b0e Mon Sep 17 00:00:00 2001 From: "Stanko K.R." Date: Mon, 10 Nov 2025 12:44:19 +0100 Subject: [PATCH 008/207] Remove the untenanted database --- app/models/identity.rb | 2 +- app/models/magic_link.rb | 2 +- app/models/membership.rb | 2 +- app/models/session.rb | 2 +- app/models/untenanted_record.rb | 4 - config/database.yml | 23 +- .../20250924190529_create_identities.rb | 0 .../20250924190729_create_memberships.rb | 0 ...51007112917_create_identity_magic_links.rb | 0 .../20251015170934_move_email_to_identity.rb | 0 ...452_associate_magic_links_to_identities.rb | 0 .../20251024081104_create_sessions.rb | 0 ...1027131911_add_join_code_to_memberships.rb | 0 ...email_addresses_mandatory_on_identities.rb | 0 ...n_identity_id_and_tenant_to_memberships.rb | 0 db/schema.rb | 31 + db/schema_cache.yml | 2863 ----------------- db/untenanted_schema.rb | 55 - 18 files changed, 45 insertions(+), 2939 deletions(-) delete mode 100644 app/models/untenanted_record.rb rename db/{untenanted_migrate => migrate}/20250924190529_create_identities.rb (100%) rename db/{untenanted_migrate => migrate}/20250924190729_create_memberships.rb (100%) rename db/{untenanted_migrate => migrate}/20251007112917_create_identity_magic_links.rb (100%) rename db/{untenanted_migrate => migrate}/20251015170934_move_email_to_identity.rb (100%) rename db/{untenanted_migrate => migrate}/20251015173452_associate_magic_links_to_identities.rb (100%) rename db/{untenanted_migrate => migrate}/20251024081104_create_sessions.rb (100%) rename db/{untenanted_migrate => migrate}/20251027131911_add_join_code_to_memberships.rb (100%) rename db/{untenanted_migrate => migrate}/20251103125952_make_email_addresses_mandatory_on_identities.rb (100%) rename db/{untenanted_migrate => migrate}/20251105082803_add_unique_constraint_on_identity_id_and_tenant_to_memberships.rb (100%) delete mode 100644 db/schema_cache.yml delete mode 100644 db/untenanted_schema.rb diff --git a/app/models/identity.rb b/app/models/identity.rb index 99c90271f..09d29d87e 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -1,4 +1,4 @@ -class Identity < UntenantedRecord +class Identity < ApplicationRecord include Transferable has_many :memberships, dependent: :destroy diff --git a/app/models/magic_link.rb b/app/models/magic_link.rb index f596a2bd3..69961e9a5 100644 --- a/app/models/magic_link.rb +++ b/app/models/magic_link.rb @@ -1,4 +1,4 @@ -class MagicLink < UntenantedRecord +class MagicLink < ApplicationRecord CODE_LENGTH = 6 EXPIRATION_TIME = 15.minutes diff --git a/app/models/membership.rb b/app/models/membership.rb index ac6e99c50..30cd658c2 100644 --- a/app/models/membership.rb +++ b/app/models/membership.rb @@ -1,4 +1,4 @@ -class Membership < UntenantedRecord +class Membership < ApplicationRecord include EmailAddressChangeable belongs_to :identity, touch: true diff --git a/app/models/session.rb b/app/models/session.rb index 92d0f20b2..63ef5cdd5 100644 --- a/app/models/session.rb +++ b/app/models/session.rb @@ -1,3 +1,3 @@ -class Session < UntenantedRecord +class Session < ApplicationRecord belongs_to :identity end diff --git a/app/models/untenanted_record.rb b/app/models/untenanted_record.rb deleted file mode 100644 index 94da45603..000000000 --- a/app/models/untenanted_record.rb +++ /dev/null @@ -1,4 +0,0 @@ -class UntenantedRecord < ActiveRecord::Base - self.abstract_class = true - connects_to database: { writing: :untenanted } -end diff --git a/config/database.yml b/config/database.yml index 79ae7d99c..b1383d571 100644 --- a/config/database.yml +++ b/config/database.yml @@ -1,25 +1,23 @@ default: &default - adapter: trilogy - host: localhost:33384 - user: root + adapter: sqlite3 pool: 50 timeout: 5000 development: primary: <<: *default - database: fizzy_development + database: storage/fizzy_development.sqlite3 cable: <<: *default - database: fizzy_development_cable + database: storage/development_cable.sqlite3 migrations_paths: db/cable_migrate cache: <<: *default - database: fizzy_development_cache + database: storage/development_cache.sqlite3 migrations_paths: db/cache_migrate queue: <<: *default - database: fizzy_development_queue + database: storage/development_queue.sqlite3 migrations_paths: db/queue_migrate # Warning: The database defined as "test" will be erased and @@ -28,24 +26,23 @@ development: test: primary: <<: *default - database: fizzy_test + database: storage/fizzy_test.sqlite3 production: &production primary: <<: *default - adapter: beamer - database: fizzy_<%= Rails.env %> + database: storage/fizzy_<%= Rails.env %>.sqlite3 cable: <<: *default - database: fizzy_<% Rails.env %>_cable.sqlite3 + database: storage/production_cable.sqlite3 migrations_paths: db/cable_migrate cache: <<: *default - database: fizzy_<% Rails.env %>_cache.sqlite3 + database: storage/production_cache.sqlite3 migrations_paths: db/cache_migrate queue: <<: *default - database: fizzy_<% Rails.env %>_queue.sqlite3 + database: storage/production_queue.sqlite3 migrations_paths: db/queue_migrate beta: *production diff --git a/db/untenanted_migrate/20250924190529_create_identities.rb b/db/migrate/20250924190529_create_identities.rb similarity index 100% rename from db/untenanted_migrate/20250924190529_create_identities.rb rename to db/migrate/20250924190529_create_identities.rb diff --git a/db/untenanted_migrate/20250924190729_create_memberships.rb b/db/migrate/20250924190729_create_memberships.rb similarity index 100% rename from db/untenanted_migrate/20250924190729_create_memberships.rb rename to db/migrate/20250924190729_create_memberships.rb diff --git a/db/untenanted_migrate/20251007112917_create_identity_magic_links.rb b/db/migrate/20251007112917_create_identity_magic_links.rb similarity index 100% rename from db/untenanted_migrate/20251007112917_create_identity_magic_links.rb rename to db/migrate/20251007112917_create_identity_magic_links.rb diff --git a/db/untenanted_migrate/20251015170934_move_email_to_identity.rb b/db/migrate/20251015170934_move_email_to_identity.rb similarity index 100% rename from db/untenanted_migrate/20251015170934_move_email_to_identity.rb rename to db/migrate/20251015170934_move_email_to_identity.rb diff --git a/db/untenanted_migrate/20251015173452_associate_magic_links_to_identities.rb b/db/migrate/20251015173452_associate_magic_links_to_identities.rb similarity index 100% rename from db/untenanted_migrate/20251015173452_associate_magic_links_to_identities.rb rename to db/migrate/20251015173452_associate_magic_links_to_identities.rb diff --git a/db/untenanted_migrate/20251024081104_create_sessions.rb b/db/migrate/20251024081104_create_sessions.rb similarity index 100% rename from db/untenanted_migrate/20251024081104_create_sessions.rb rename to db/migrate/20251024081104_create_sessions.rb diff --git a/db/untenanted_migrate/20251027131911_add_join_code_to_memberships.rb b/db/migrate/20251027131911_add_join_code_to_memberships.rb similarity index 100% rename from db/untenanted_migrate/20251027131911_add_join_code_to_memberships.rb rename to db/migrate/20251027131911_add_join_code_to_memberships.rb diff --git a/db/untenanted_migrate/20251103125952_make_email_addresses_mandatory_on_identities.rb b/db/migrate/20251103125952_make_email_addresses_mandatory_on_identities.rb similarity index 100% rename from db/untenanted_migrate/20251103125952_make_email_addresses_mandatory_on_identities.rb rename to db/migrate/20251103125952_make_email_addresses_mandatory_on_identities.rb diff --git a/db/untenanted_migrate/20251105082803_add_unique_constraint_on_identity_id_and_tenant_to_memberships.rb b/db/migrate/20251105082803_add_unique_constraint_on_identity_id_and_tenant_to_memberships.rb similarity index 100% rename from db/untenanted_migrate/20251105082803_add_unique_constraint_on_identity_id_and_tenant_to_memberships.rb rename to db/migrate/20251105082803_add_unique_constraint_on_identity_id_and_tenant_to_memberships.rb diff --git a/db/schema.rb b/db/schema.rb index 9516f3206..4086b41ba 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -262,6 +262,35 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_06_154151) do t.index ["tag_id"], name: "index_filters_tags_on_tag_id" end + create_table "identities", force: :cascade do |t| + t.datetime "created_at", null: false + t.string "email_address", null: false + t.datetime "updated_at", null: false + t.index ["email_address"], name: "index_identities_on_email_address", unique: true + end + + create_table "magic_links", force: :cascade do |t| + t.string "code", null: false + t.datetime "created_at", null: false + t.datetime "expires_at", null: false + t.integer "identity_id" + t.datetime "updated_at", null: false + t.index ["code"], name: "index_magic_links_on_code", unique: true + t.index ["expires_at"], name: "index_magic_links_on_expires_at" + t.index ["identity_id"], name: "index_magic_links_on_identity_id" + end + + create_table "memberships", force: :cascade do |t| + t.datetime "created_at", null: false + t.integer "identity_id", null: false + t.string "join_code" + t.string "tenant", null: false + t.datetime "updated_at", null: false + t.index ["identity_id"], name: "index_memberships_on_identity_id" + t.index ["tenant", "identity_id"], name: "index_memberships_on_tenant_and_identity_id", unique: true + t.index ["tenant"], name: "index_memberships_on_user_tenant_and_user_id" + end + create_table "mentions", force: :cascade do |t| t.datetime "created_at", null: false t.integer "mentionee_id", null: false @@ -455,6 +484,8 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_06_154151) do add_foreign_key "columns", "boards" add_foreign_key "comments", "cards" add_foreign_key "events", "boards" + add_foreign_key "magic_links", "identities" + add_foreign_key "memberships", "identities" add_foreign_key "mentions", "users", column: "mentionee_id" add_foreign_key "mentions", "users", column: "mentioner_id" add_foreign_key "notification_bundles", "users" diff --git a/db/schema_cache.yml b/db/schema_cache.yml deleted file mode 100644 index f818b58bf..000000000 --- a/db/schema_cache.yml +++ /dev/null @@ -1,2863 +0,0 @@ ---- !ruby/object:ActiveRecord::ConnectionAdapters::SchemaCache -columns: - accesses: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: accessed_at - cast_type: &1 !ruby/object:ActiveRecord::Type::DateTime - precision: 6 - scale: - limit: - timezone: - sql_type_metadata: &2 !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata - sql_type: datetime(6) - type: :datetime - limit: - precision: 6 - scale: - 'null': true - default: - default_function: - collation: - comment: - - &22 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: board_id - cast_type: &3 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3Adapter::SQLite3Integer - precision: - scale: - limit: - max: 9223372036854775808 - min: -9223372036854775808 - sql_type_metadata: &4 !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata - sql_type: INTEGER - type: :integer - limit: - precision: - scale: - 'null': false - default: - default_function: - collation: - comment: - - &7 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: created_at - cast_type: *1 - sql_type_metadata: *2 - 'null': false - default: - default_function: - collation: - comment: - - &8 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: true - name: id - cast_type: *3 - sql_type_metadata: *4 - 'null': false - default: - default_function: - collation: - comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: involvement - cast_type: &5 !ruby/object:ActiveModel::Type::String - true: t - false: f - precision: - scale: - limit: - sql_type_metadata: &6 !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata - sql_type: varchar - type: :string - limit: - precision: - scale: - 'null': false - default: access_only - default_function: - collation: - comment: - - &9 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: updated_at - cast_type: *1 - sql_type_metadata: *2 - 'null': false - default: - default_function: - collation: - comment: - - &28 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: user_id - cast_type: *3 - sql_type_metadata: *4 - 'null': false - default: - default_function: - collation: - comment: - account_join_codes: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: code - cast_type: *5 - sql_type_metadata: *6 - 'null': false - default: - default_function: - collation: - comment: - - *7 - - *8 - - *9 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: usage_count - cast_type: *3 - sql_type_metadata: *4 - 'null': false - default: 0 - default_function: - collation: - comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: usage_limit - cast_type: *3 - sql_type_metadata: *4 - 'null': false - default: 10 - default_function: - collation: - comment: - accounts: - - *7 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: external_account_id - cast_type: *3 - sql_type_metadata: *4 - 'null': true - default: - default_function: - collation: - comment: - - *8 - - &10 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: name - cast_type: *5 - sql_type_metadata: *6 - 'null': false - default: - default_function: - collation: - comment: - - *9 - action_text_rich_texts: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: body - cast_type: &15 !ruby/object:ActiveRecord::Type::Text - true: t - false: f - precision: - scale: - limit: - sql_type_metadata: &16 !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata - sql_type: TEXT - type: :text - limit: - precision: - scale: - 'null': true - default: - default_function: - collation: - comment: - - *7 - - *8 - - *10 - - &13 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: record_id - cast_type: &11 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3Adapter::SQLite3Integer - precision: - scale: - limit: - max: 9223372036854775808 - min: -9223372036854775808 - sql_type_metadata: &12 !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata - sql_type: bigint - type: :integer - limit: - precision: - scale: - 'null': false - default: - default_function: - collation: - comment: - - &14 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: record_type - cast_type: *5 - sql_type_metadata: *6 - 'null': false - default: - default_function: - collation: - comment: - - *9 - active_storage_attachments: - - &17 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: blob_id - cast_type: *11 - sql_type_metadata: *12 - 'null': false - default: - default_function: - collation: - comment: - - *7 - - *8 - - *10 - - *13 - - *14 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: slug - cast_type: *5 - sql_type_metadata: *6 - 'null': true - default: - default_function: - collation: - comment: - active_storage_blobs: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: byte_size - cast_type: *11 - sql_type_metadata: *12 - 'null': false - default: - default_function: - collation: - comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: checksum - cast_type: *5 - sql_type_metadata: *6 - 'null': true - default: - default_function: - collation: - comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: content_type - cast_type: *5 - sql_type_metadata: *6 - 'null': true - default: - default_function: - collation: - comment: - - *7 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: filename - cast_type: *5 - sql_type_metadata: *6 - 'null': false - default: - default_function: - collation: - comment: - - *8 - - &18 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: key - cast_type: *5 - sql_type_metadata: *6 - 'null': false - default: - default_function: - collation: - comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: metadata - cast_type: *15 - sql_type_metadata: *16 - 'null': true - default: - default_function: - collation: - comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: service_name - cast_type: *5 - sql_type_metadata: *6 - 'null': false - default: - default_function: - collation: - comment: - active_storage_variant_records: - - *17 - - *8 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: variation_digest - cast_type: *5 - sql_type_metadata: *6 - 'null': false - default: - default_function: - collation: - comment: - ar_internal_metadata: - - *7 - - *18 - - *9 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: value - cast_type: *5 - sql_type_metadata: *6 - 'null': true - default: - default_function: - collation: - comment: - assignees_filters: - - &20 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: assignee_id - cast_type: *3 - sql_type_metadata: *4 - 'null': false - default: - default_function: - collation: - comment: - - &19 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: filter_id - cast_type: *3 - sql_type_metadata: *4 - 'null': false - default: - default_function: - collation: - comment: - assigners_filters: - - &21 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: assigner_id - cast_type: *3 - sql_type_metadata: *4 - 'null': false - default: - default_function: - collation: - comment: - - *19 - assignments: - - *20 - - *21 - - &23 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: card_id - cast_type: *3 - sql_type_metadata: *4 - 'null': false - default: - default_function: - collation: - comment: - - *7 - - *8 - - *9 - board_publications: - - *22 - - *7 - - *8 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: key - cast_type: *5 - sql_type_metadata: *6 - 'null': true - default: - default_function: - collation: - comment: - - *9 - boards: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: all_access - cast_type: &31 !ruby/object:ActiveModel::Type::Boolean - precision: - scale: - limit: - sql_type_metadata: &32 !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata - sql_type: boolean - type: :boolean - limit: - precision: - scale: - 'null': false - default: false - default_function: - collation: - comment: - - *7 - - &24 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: creator_id - cast_type: *3 - sql_type_metadata: *4 - 'null': false - default: - default_function: - collation: - comment: - - *8 - - *10 - - *9 - boards_filters: - - *22 - - *19 - card_activity_spikes: - - *23 - - *7 - - *8 - - *9 - card_engagements: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: card_id - cast_type: *3 - sql_type_metadata: *4 - 'null': true - default: - default_function: - collation: - comment: - - *7 - - *8 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: status - cast_type: *5 - sql_type_metadata: *6 - 'null': false - default: doing - default_function: - collation: - comment: - - *9 - card_goldnesses: - - *23 - - *7 - - *8 - - *9 - card_not_nows: - - *23 - - *7 - - *8 - - *9 - - &25 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: user_id - cast_type: *3 - sql_type_metadata: *4 - 'null': true - default: - default_function: - collation: - comment: - cards: - - *22 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: column_id - cast_type: *3 - sql_type_metadata: *4 - 'null': true - default: - default_function: - collation: - comment: - - *7 - - *24 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: due_on - cast_type: !ruby/object:ActiveRecord::Type::Date - precision: - scale: - limit: - timezone: - sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata - sql_type: date - type: :date - limit: - precision: - scale: - 'null': true - default: - default_function: - collation: - comment: - - *8 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: last_active_at - cast_type: *1 - sql_type_metadata: *2 - 'null': false - default: - default_function: - collation: - comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: status - cast_type: *15 - sql_type_metadata: *16 - 'null': false - default: drafted - default_function: - collation: - comment: - - &34 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: title - cast_type: *5 - sql_type_metadata: *6 - 'null': true - default: - default_function: - collation: - comment: - - *9 - closers_filters: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: closer_id - cast_type: *3 - sql_type_metadata: *4 - 'null': false - default: - default_function: - collation: - comment: - - *19 - closures: - - *23 - - *7 - - *8 - - *9 - - *25 - columns: - - *22 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: color - cast_type: *5 - sql_type_metadata: *6 - 'null': false - default: - default_function: - collation: - comment: - - *7 - - *8 - - *10 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: position - cast_type: *3 - sql_type_metadata: *4 - 'null': false - default: 0 - default_function: - collation: - comment: - - *9 - comments: - - *23 - - *7 - - *24 - - *8 - - *9 - creators_filters: - - *24 - - *19 - entropies: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: auto_postpone_period - cast_type: *11 - sql_type_metadata: *12 - 'null': false - default: 2592000 - default_function: - collation: - comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: container_id - cast_type: *3 - sql_type_metadata: *4 - 'null': false - default: - default_function: - collation: - comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: container_type - cast_type: *5 - sql_type_metadata: *6 - 'null': false - default: - default_function: - collation: - comment: - - *7 - - *8 - - *9 - events: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: action - cast_type: *5 - sql_type_metadata: *6 - 'null': false - default: - default_function: - collation: - comment: - - *22 - - *7 - - *24 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: eventable_id - cast_type: *3 - sql_type_metadata: *4 - 'null': false - default: - default_function: - collation: - comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: eventable_type - cast_type: *5 - sql_type_metadata: *6 - 'null': false - default: - default_function: - collation: - comment: - - *8 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: particulars - cast_type: &26 !ruby/object:ActiveRecord::Type::Json - precision: - scale: - limit: - sql_type_metadata: &27 !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata - sql_type: json - type: :json - limit: - precision: - scale: - 'null': true - default: "{}" - default_function: - collation: - comment: - - *9 - filters: - - *7 - - *24 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: fields - cast_type: *26 - sql_type_metadata: *27 - 'null': false - default: "{}" - default_function: - collation: - comment: - - *8 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: params_digest - cast_type: *5 - sql_type_metadata: *6 - 'null': false - default: - default_function: - collation: - comment: - - *9 - filters_tags: - - *19 - - &33 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: tag_id - cast_type: *3 - sql_type_metadata: *4 - 'null': false - default: - default_function: - collation: - comment: - mentions: - - *7 - - *8 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: mentionee_id - cast_type: *3 - sql_type_metadata: *4 - 'null': false - default: - default_function: - collation: - comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: mentioner_id - cast_type: *3 - sql_type_metadata: *4 - 'null': false - default: - default_function: - collation: - comment: - - &29 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: source_id - cast_type: *3 - sql_type_metadata: *4 - 'null': false - default: - default_function: - collation: - comment: - - &30 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: source_type - cast_type: *5 - sql_type_metadata: *6 - 'null': false - default: - default_function: - collation: - comment: - - *9 - notification_bundles: - - *7 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: ends_at - cast_type: *1 - sql_type_metadata: *2 - 'null': false - default: - default_function: - collation: - comment: - - *8 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: starts_at - cast_type: *1 - sql_type_metadata: *2 - 'null': false - default: - default_function: - collation: - comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: status - cast_type: *3 - sql_type_metadata: *4 - 'null': false - default: 0 - default_function: - collation: - comment: - - *9 - - *28 - notifications: - - *7 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: creator_id - cast_type: *3 - sql_type_metadata: *4 - 'null': true - default: - default_function: - collation: - comment: - - *8 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: read_at - cast_type: *1 - sql_type_metadata: *2 - 'null': true - default: - default_function: - collation: - comment: - - *29 - - *30 - - *9 - - *28 - pins: - - *23 - - *7 - - *8 - - *9 - - *28 - push_subscriptions: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: auth_key - cast_type: *5 - sql_type_metadata: *6 - 'null': true - default: - default_function: - collation: - comment: - - *7 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: endpoint - cast_type: *5 - sql_type_metadata: *6 - 'null': true - default: - default_function: - collation: - comment: - - *8 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: p256dh_key - cast_type: *5 - sql_type_metadata: *6 - 'null': true - default: - default_function: - collation: - comment: - - *9 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: user_agent - cast_type: *5 - sql_type_metadata: *6 - 'null': true - default: - default_function: - collation: - comment: - - *28 - reactions: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: comment_id - cast_type: *3 - sql_type_metadata: *4 - 'null': false - default: - default_function: - collation: - comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: content - cast_type: !ruby/object:ActiveModel::Type::String - true: t - false: f - precision: - scale: - limit: 16 - sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata - sql_type: varchar(16) - type: :string - limit: 16 - precision: - scale: - 'null': false - default: - default_function: - collation: - comment: - - *7 - - *8 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: reacter_id - cast_type: *3 - sql_type_metadata: *4 - 'null': false - default: - default_function: - collation: - comment: - - *9 - schema_migrations: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: version - cast_type: *5 - sql_type_metadata: *6 - 'null': false - default: - default_function: - collation: - comment: - search_queries: - - *7 - - *8 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: terms - cast_type: !ruby/object:ActiveModel::Type::String - true: t - false: f - precision: - scale: - limit: 2000 - sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata - sql_type: varchar(2000) - type: :string - limit: 2000 - precision: - scale: - 'null': false - default: - default_function: - collation: - comment: - - *9 - - *28 - search_results: - - *7 - - *8 - - *9 - steps: - - *23 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: completed - cast_type: *31 - sql_type_metadata: *32 - 'null': false - default: false - default_function: - collation: - comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: content - cast_type: *15 - sql_type_metadata: *16 - 'null': false - default: - default_function: - collation: - comment: - - *7 - - *8 - - *9 - taggings: - - *23 - - *7 - - *8 - - *33 - - *9 - tags: - - *7 - - *8 - - *34 - - *9 - user_settings: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: bundle_email_frequency - cast_type: *3 - sql_type_metadata: *4 - 'null': false - default: 0 - default_function: - collation: - comment: - - *7 - - *8 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: timezone_name - cast_type: *5 - sql_type_metadata: *6 - 'null': true - default: - default_function: - collation: - comment: - - *9 - - *28 - users: - - &36 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: active - cast_type: *31 - sql_type_metadata: *32 - 'null': false - default: true - default_function: - collation: - comment: - - *7 - - *8 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: membership_id - cast_type: *3 - sql_type_metadata: *4 - 'null': true - default: - default_function: - collation: - comment: - - *10 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: role - cast_type: *5 - sql_type_metadata: *6 - 'null': false - default: member - default_function: - collation: - comment: - - *9 - watches: - - *23 - - *7 - - *8 - - *9 - - *28 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: watching - cast_type: *31 - sql_type_metadata: *32 - 'null': false - default: true - default_function: - collation: - comment: - webhook_delinquency_trackers: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: consecutive_failures_count - cast_type: *3 - sql_type_metadata: *4 - 'null': true - default: 0 - default_function: - collation: - comment: - - *7 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: first_failure_at - cast_type: *1 - sql_type_metadata: *2 - 'null': true - default: - default_function: - collation: - comment: - - *8 - - *9 - - &35 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: webhook_id - cast_type: *3 - sql_type_metadata: *4 - 'null': false - default: - default_function: - collation: - comment: - webhook_deliveries: - - *7 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: event_id - cast_type: *3 - sql_type_metadata: *4 - 'null': false - default: - default_function: - collation: - comment: - - *8 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: request - cast_type: *15 - sql_type_metadata: *16 - 'null': true - default: - default_function: - collation: - comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: response - cast_type: *15 - sql_type_metadata: *16 - 'null': true - default: - default_function: - collation: - comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: state - cast_type: *5 - sql_type_metadata: *6 - 'null': false - default: - default_function: - collation: - comment: - - *9 - - *35 - webhooks: - - *36 - - *22 - - *7 - - *8 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: name - cast_type: *5 - sql_type_metadata: *6 - 'null': true - default: - default_function: - collation: - comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: signing_secret - cast_type: *5 - sql_type_metadata: *6 - 'null': false - default: - default_function: - collation: - comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: subscribed_actions - cast_type: *15 - sql_type_metadata: *16 - 'null': true - default: - default_function: - collation: - comment: - - *9 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: url - cast_type: *15 - sql_type_metadata: *16 - 'null': false - default: - default_function: - collation: - comment: -primary_keys: - accesses: id - account_join_codes: id - accounts: id - action_text_rich_texts: id - active_storage_attachments: id - active_storage_blobs: id - active_storage_variant_records: id - ar_internal_metadata: key - assignees_filters: - assigners_filters: - assignments: id - board_publications: id - boards: id - boards_filters: - card_activity_spikes: id - card_engagements: id - card_goldnesses: id - card_not_nows: id - cards: id - closers_filters: - closures: id - columns: id - comments: id - creators_filters: - entropies: id - events: id - filters: id - filters_tags: - mentions: id - notification_bundles: id - notifications: id - pins: id - push_subscriptions: id - reactions: id - schema_migrations: version - search_queries: id - search_results: id - steps: id - taggings: id - tags: id - user_settings: id - users: id - watches: id - webhook_delinquency_trackers: id - webhook_deliveries: id - webhooks: id -data_sources: - accesses: true - account_join_codes: true - accounts: true - action_text_rich_texts: true - active_storage_attachments: true - active_storage_blobs: true - active_storage_variant_records: true - ar_internal_metadata: true - assignees_filters: true - assigners_filters: true - assignments: true - board_publications: true - boards: true - boards_filters: true - card_activity_spikes: true - card_engagements: true - card_goldnesses: true - card_not_nows: true - cards: true - closers_filters: true - closures: true - columns: true - comments: true - creators_filters: true - entropies: true - events: true - filters: true - filters_tags: true - mentions: true - notification_bundles: true - notifications: true - pins: true - push_subscriptions: true - reactions: true - schema_migrations: true - search_queries: true - search_results: true - steps: true - taggings: true - tags: true - user_settings: true - users: true - watches: true - webhook_delinquency_trackers: true - webhook_deliveries: true - webhooks: true -indexes: - accesses: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: accesses - name: index_accesses_on_accessed_at - unique: false - columns: - - accessed_at - lengths: {} - orders: :desc - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: accesses - name: index_accesses_on_board_id - unique: false - columns: - - board_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: accesses - name: index_accesses_on_board_id_and_user_id - unique: true - columns: - - board_id - - user_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: accesses - name: index_accesses_on_user_id - unique: false - columns: - - user_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - account_join_codes: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: account_join_codes - name: index_account_join_codes_on_code - unique: true - columns: - - code - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - accounts: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: accounts - name: index_accounts_on_external_account_id - unique: true - columns: - - external_account_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - action_text_rich_texts: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: action_text_rich_texts - name: index_action_text_rich_texts_uniqueness - unique: true - columns: - - record_type - - record_id - - name - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - active_storage_attachments: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: active_storage_attachments - name: index_active_storage_attachments_on_blob_id - unique: false - columns: - - blob_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: active_storage_attachments - name: index_active_storage_attachments_on_slug - unique: true - columns: - - slug - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: active_storage_attachments - name: index_active_storage_attachments_uniqueness - unique: true - columns: - - record_type - - record_id - - name - - blob_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - active_storage_blobs: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: active_storage_blobs - name: index_active_storage_blobs_on_key - unique: true - columns: - - key - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - active_storage_variant_records: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: active_storage_variant_records - name: index_active_storage_variant_records_uniqueness - unique: true - columns: - - blob_id - - variation_digest - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - ar_internal_metadata: [] - assignees_filters: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: assignees_filters - name: index_assignees_filters_on_assignee_id - unique: false - columns: - - assignee_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: assignees_filters - name: index_assignees_filters_on_filter_id - unique: false - columns: - - filter_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - assigners_filters: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: assigners_filters - name: index_assigners_filters_on_assigner_id - unique: false - columns: - - assigner_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: assigners_filters - name: index_assigners_filters_on_filter_id - unique: false - columns: - - filter_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - assignments: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: assignments - name: index_assignments_on_assignee_id_and_card_id - unique: true - columns: - - assignee_id - - card_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: assignments - name: index_assignments_on_card_id - unique: false - columns: - - card_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - board_publications: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: board_publications - name: index_board_publications_on_board_id - unique: false - columns: - - board_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: board_publications - name: index_board_publications_on_key - unique: true - columns: - - key - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - boards: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: boards - name: index_boards_on_creator_id - unique: false - columns: - - creator_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - boards_filters: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: boards_filters - name: index_boards_filters_on_board_id - unique: false - columns: - - board_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: boards_filters - name: index_boards_filters_on_filter_id - unique: false - columns: - - filter_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - card_activity_spikes: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: card_activity_spikes - name: index_card_activity_spikes_on_card_id - unique: false - columns: - - card_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - card_engagements: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: card_engagements - name: index_card_engagements_on_card_id - unique: false - columns: - - card_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: card_engagements - name: index_card_engagements_on_status - unique: false - columns: - - status - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - card_goldnesses: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: card_goldnesses - name: index_card_goldnesses_on_card_id - unique: true - columns: - - card_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - card_not_nows: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: card_not_nows - name: index_card_not_nows_on_card_id - unique: true - columns: - - card_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: card_not_nows - name: index_card_not_nows_on_user_id - unique: false - columns: - - user_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - cards: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: cards - name: index_cards_on_board_id - unique: false - columns: - - board_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: cards - name: index_cards_on_column_id - unique: false - columns: - - column_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: cards - name: index_cards_on_last_active_at_and_status - unique: false - columns: - - last_active_at - - status - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - closers_filters: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: closers_filters - name: index_closers_filters_on_closer_id - unique: false - columns: - - closer_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: closers_filters - name: index_closers_filters_on_filter_id - unique: false - columns: - - filter_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - closures: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: closures - name: index_closures_on_card_id - unique: true - columns: - - card_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: closures - name: index_closures_on_card_id_and_created_at - unique: false - columns: - - card_id - - created_at - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: closures - name: index_closures_on_user_id - unique: false - columns: - - user_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - columns: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: columns - name: index_columns_on_board_id - unique: false - columns: - - board_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: columns - name: index_columns_on_board_id_and_position - unique: false - columns: - - board_id - - position - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - comments: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: comments - name: index_comments_on_card_id - unique: false - columns: - - card_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - creators_filters: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: creators_filters - name: index_creators_filters_on_creator_id - unique: false - columns: - - creator_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: creators_filters - name: index_creators_filters_on_filter_id - unique: false - columns: - - filter_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - entropies: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: entropies - name: idx_on_container_type_container_id_auto_postpone_pe_3d79b50517 - unique: false - columns: - - container_type - - container_id - - auto_postpone_period - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: entropies - name: index_entropy_configurations_on_container - unique: true - columns: - - container_type - - container_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - events: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: events - name: index_events_on_board_id - unique: false - columns: - - board_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: events - name: index_events_on_board_id_and_action_and_created_at - unique: false - columns: - - board_id - - action - - created_at - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: events - name: index_events_on_creator_id - unique: false - columns: - - creator_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: events - name: index_events_on_eventable - unique: false - columns: - - eventable_type - - eventable_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: events - name: index_events_on_summary_id_and_action - unique: false - columns: - - action - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - filters: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: filters - name: index_filters_on_creator_id_and_params_digest - unique: true - columns: - - creator_id - - params_digest - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - filters_tags: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: filters_tags - name: index_filters_tags_on_filter_id - unique: false - columns: - - filter_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: filters_tags - name: index_filters_tags_on_tag_id - unique: false - columns: - - tag_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - mentions: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: mentions - name: index_mentions_on_mentionee_id - unique: false - columns: - - mentionee_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: mentions - name: index_mentions_on_mentioner_id - unique: false - columns: - - mentioner_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: mentions - name: index_mentions_on_source - unique: false - columns: - - source_type - - source_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - notification_bundles: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: notification_bundles - name: idx_on_user_id_starts_at_ends_at_7eae5d3ac5 - unique: false - columns: - - user_id - - starts_at - - ends_at - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: notification_bundles - name: index_notification_bundles_on_ends_at_and_status - unique: false - columns: - - ends_at - - status - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: notification_bundles - name: index_notification_bundles_on_user_id_and_status - unique: false - columns: - - user_id - - status - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - notifications: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: notifications - name: index_notifications_on_creator_id - unique: false - columns: - - creator_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: notifications - name: index_notifications_on_source - unique: false - columns: - - source_type - - source_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: notifications - name: index_notifications_on_user_id - unique: false - columns: - - user_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: notifications - name: index_notifications_on_user_id_and_read_at_and_created_at - unique: false - columns: - - user_id - - read_at - - created_at - lengths: {} - orders: - read_at: :desc - created_at: :desc - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - pins: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: pins - name: index_pins_on_card_id - unique: false - columns: - - card_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: pins - name: index_pins_on_card_id_and_user_id - unique: true - columns: - - card_id - - user_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: pins - name: index_pins_on_user_id - unique: false - columns: - - user_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - push_subscriptions: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: push_subscriptions - name: idx_on_endpoint_p256dh_key_auth_key_7553014576 - unique: false - columns: - - endpoint - - p256dh_key - - auth_key - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: push_subscriptions - name: index_push_subscriptions_on_endpoint - unique: false - columns: - - endpoint - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: push_subscriptions - name: index_push_subscriptions_on_user_agent - unique: false - columns: - - user_agent - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: push_subscriptions - name: index_push_subscriptions_on_user_id - unique: false - columns: - - user_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: push_subscriptions - name: index_push_subscriptions_on_user_id_and_endpoint - unique: true - columns: - - user_id - - endpoint - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - reactions: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: reactions - name: index_reactions_on_comment_id - unique: false - columns: - - comment_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: reactions - name: index_reactions_on_reacter_id - unique: false - columns: - - reacter_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - schema_migrations: [] - search_queries: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: search_queries - name: index_search_queries_on_user_id - unique: false - columns: - - user_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: search_queries - name: index_search_queries_on_user_id_and_terms - unique: false - columns: - - user_id - - terms - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: search_queries - name: index_search_queries_on_user_id_and_updated_at - unique: true - columns: - - user_id - - updated_at - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - search_results: [] - steps: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: steps - name: index_steps_on_card_id - unique: false - columns: - - card_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: steps - name: index_steps_on_card_id_and_completed - unique: false - columns: - - card_id - - completed - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - taggings: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: taggings - name: index_taggings_on_card_id_and_tag_id - unique: true - columns: - - card_id - - tag_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: taggings - name: index_taggings_on_tag_id - unique: false - columns: - - tag_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - tags: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: tags - name: index_tags_on_title - unique: true - columns: - - title - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - user_settings: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: user_settings - name: index_user_settings_on_user_id - unique: false - columns: - - user_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: user_settings - name: index_user_settings_on_user_id_and_bundle_email_frequency - unique: false - columns: - - user_id - - bundle_email_frequency - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - users: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: users - name: index_users_on_membership_id - unique: false - columns: - - membership_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: users - name: index_users_on_role - unique: false - columns: - - role - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - watches: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: watches - name: index_watches_on_card_id - unique: false - columns: - - card_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: watches - name: index_watches_on_user_id - unique: false - columns: - - user_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: watches - name: index_watches_on_user_id_and_card_id - unique: false - columns: - - user_id - - card_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - webhook_delinquency_trackers: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: webhook_delinquency_trackers - name: index_webhook_delinquency_trackers_on_webhook_id - unique: false - columns: - - webhook_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - webhook_deliveries: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: webhook_deliveries - name: index_webhook_deliveries_on_event_id - unique: false - columns: - - event_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: webhook_deliveries - name: index_webhook_deliveries_on_webhook_id - unique: false - columns: - - webhook_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - webhooks: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: webhooks - name: index_webhooks_on_board_id - unique: false - columns: - - board_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: webhooks - name: index_webhooks_on_subscribed_actions - unique: false - columns: - - subscribed_actions - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true -version: 20251106154151 diff --git a/db/untenanted_schema.rb b/db/untenanted_schema.rb deleted file mode 100644 index efe4c961b..000000000 --- a/db/untenanted_schema.rb +++ /dev/null @@ -1,55 +0,0 @@ -# This file is auto-generated from the current state of the database. Instead -# of editing this file, please use the migrations feature of Active Record to -# incrementally modify your database, and then regenerate this schema definition. -# -# This file is the source Rails uses to define your schema when running `bin/rails -# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to -# be faster and is potentially less error prone than running all of your -# migrations from scratch. Old migrations may fail to apply correctly if those -# migrations use external dependencies or application code. -# -# It's strongly recommended that you check this file into your version control system. - -ActiveRecord::Schema[8.2].define(version: 2025_11_05_082803) do - create_table "identities", force: :cascade do |t| - t.datetime "created_at", null: false - t.string "email_address", null: false - t.datetime "updated_at", null: false - t.index ["email_address"], name: "index_identities_on_email_address", unique: true - end - - create_table "magic_links", force: :cascade do |t| - t.string "code", null: false - t.datetime "created_at", null: false - t.datetime "expires_at", null: false - t.integer "identity_id" - t.datetime "updated_at", null: false - t.index ["code"], name: "index_magic_links_on_code", unique: true - t.index ["expires_at"], name: "index_magic_links_on_expires_at" - t.index ["identity_id"], name: "index_magic_links_on_identity_id" - end - - create_table "memberships", force: :cascade do |t| - t.datetime "created_at", null: false - t.integer "identity_id", null: false - t.string "join_code" - t.string "tenant", null: false - t.datetime "updated_at", null: false - t.index ["identity_id"], name: "index_memberships_on_identity_id" - t.index ["tenant", "identity_id"], name: "index_memberships_on_tenant_and_identity_id", unique: true - t.index ["tenant"], name: "index_memberships_on_user_tenant_and_user_id" - end - - create_table "sessions", force: :cascade do |t| - t.datetime "created_at", null: false - t.integer "identity_id", null: false - t.string "ip_address" - t.datetime "updated_at", null: false - t.string "user_agent" - t.index ["identity_id"], name: "index_sessions_on_identity_id" - end - - add_foreign_key "magic_links", "identities" - add_foreign_key "memberships", "identities" - add_foreign_key "sessions", "identities" -end From d41d50d52bc44fcde4f7b16b492e9496f80a7d5a Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Mon, 10 Nov 2025 07:58:40 -0500 Subject: [PATCH 009/207] =?UTF-8?q?Account.sole=20=E2=86=92=20Current.acco?= =?UTF-8?q?unt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit and some other de-tenant changes, including removing the controller tenanting concerns --- app/channels/application_cable/connection.rb | 5 +++-- .../account/entropies_controller.rb | 2 +- .../account/settings_controller.rb | 2 +- app/controllers/concerns/authentication.rb | 21 +------------------ app/controllers/concerns/authorization.rb | 2 +- app/controllers/join_codes_controller.rb | 6 +++--- .../confirmations_controller.rb | 1 - .../memberships/email_addresses_controller.rb | 2 -- .../memberships/unlink_controller.rb | 1 - app/controllers/pwa_controller.rb | 1 - .../sessions/magic_links_controller.rb | 1 - app/controllers/sessions/menus_controller.rb | 2 -- .../sessions/transfers_controller.rb | 1 - app/controllers/sessions_controller.rb | 1 - app/helpers/application_helper.rb | 4 ++-- app/mailers/application_mailer.rb | 4 ++-- app/models/board/entropic.rb | 2 +- app/models/card/entropic.rb | 6 +++--- app/models/card/promptable.rb | 2 +- app/models/comment/promptable.rb | 2 +- app/models/current.rb | 9 ++++---- app/models/membership.rb | 9 +++----- app/models/notification_pusher.rb | 6 +++--- app/views/layouts/shared/_head.html.erb | 2 +- app/views/my/menus/show.html.erb | 2 +- app/views/public/boards/show.html.erb | 4 ++-- app/views/pwa/manifest.json.erb | 2 +- app/views/sessions/starts/new.html.erb | 2 +- bin/setup | 4 ++-- db/seeds.rb | 3 +-- lib/tasks/seed.rake | 9 ++++---- script/create-account.rb | 2 +- script/load-prod-db-in-dev.rb | 4 ++-- .../20250924-populate-identities.rb | 4 ++-- .../migrate-content-to-slugged-urls.rb | 4 ++-- .../accounts/settings_controller_test.rb | 2 +- .../previews/identity_mailer_preview.rb | 21 ++++++++++--------- test/models/account/seedeable_test.rb | 2 +- test/models/account_test.rb | 2 +- test/models/entropy_test.rb | 2 +- 40 files changed, 67 insertions(+), 96 deletions(-) diff --git a/app/channels/application_cable/connection.rb b/app/channels/application_cable/connection.rb index f1e997503..a01c27bc2 100644 --- a/app/channels/application_cable/connection.rb +++ b/app/channels/application_cable/connection.rb @@ -10,8 +10,9 @@ module ApplicationCable private def set_current_user if session = find_session_by_cookie - membership = session.identity.memberships.find_by!(tenant: current_tenant) - self.current_user = membership.user if membership.user.active? + # # TODO:PLANB: not sure how to fix this + # membership = session.identity.memberships.find_by!(tenant: current_tenant) + # self.current_user = membership.user if membership.user.active? end end diff --git a/app/controllers/account/entropies_controller.rb b/app/controllers/account/entropies_controller.rb index 00d195584..9876e99d8 100644 --- a/app/controllers/account/entropies_controller.rb +++ b/app/controllers/account/entropies_controller.rb @@ -2,7 +2,7 @@ class Account::EntropiesController < ApplicationController before_action :ensure_admin def update - Account.sole.entropy.update!(entropy_params) + Current.account.entropy.update!(entropy_params) redirect_to account_settings_path, notice: "Account updated" end diff --git a/app/controllers/account/settings_controller.rb b/app/controllers/account/settings_controller.rb index 42a6a60f7..bbfec0fe1 100644 --- a/app/controllers/account/settings_controller.rb +++ b/app/controllers/account/settings_controller.rb @@ -13,7 +13,7 @@ class Account::SettingsController < ApplicationController private def set_account - @account = Account.sole + @account = Current.account end def account_params diff --git a/app/controllers/concerns/authentication.rb b/app/controllers/concerns/authentication.rb index 5afbcccf8..9577ccb1d 100644 --- a/app/controllers/concerns/authentication.rb +++ b/app/controllers/concerns/authentication.rb @@ -2,8 +2,6 @@ module Authentication extend ActiveSupport::Concern included do - # Checking for tenant must happen first so we redirect before trying to access the db. - before_action :require_tenant prepend_before_action :clear_old_scoped_session_cookies before_action :require_authentication @@ -25,11 +23,6 @@ module Authentication before_action :resume_session, **options allow_unauthorized_access **options end - - def require_untenanted_access(**options) - skip_before_action :require_tenant, **options - before_action :redirect_tenanted_request, **options - end end private @@ -37,12 +30,6 @@ module Authentication Current.session.present? end - def require_tenant - if ApplicationRecord.current_tenant.blank? - redirect_to session_menu_url(script_name: nil) - end - end - def require_authentication resume_session || request_authentication end @@ -65,9 +52,7 @@ module Authentication end def request_authentication - if ApplicationRecord.current_tenant.present? - session[:return_to_after_authenticating] = request.url - end + session[:return_to_after_authenticating] = request.url redirect_to_login_url end @@ -80,10 +65,6 @@ module Authentication redirect_to root_url if authenticated? end - def redirect_tenanted_request - redirect_to root_url if ApplicationRecord.current_tenant - end - def start_new_session_for(identity) identity.sessions.create!(user_agent: request.user_agent, ip_address: request.remote_ip).tap do |session| set_current_session session diff --git a/app/controllers/concerns/authorization.rb b/app/controllers/concerns/authorization.rb index 6d31f844e..dfd0e946d 100644 --- a/app/controllers/concerns/authorization.rb +++ b/app/controllers/concerns/authorization.rb @@ -2,7 +2,7 @@ module Authorization extend ActiveSupport::Concern included do - before_action :ensure_can_access_account, if: -> { ApplicationRecord.current_tenant && authenticated? } + before_action :ensure_can_access_account, if: -> { authenticated? } end class_methods do diff --git a/app/controllers/join_codes_controller.rb b/app/controllers/join_codes_controller.rb index a4b6768b9..dcceb9f60 100644 --- a/app/controllers/join_codes_controller.rb +++ b/app/controllers/join_codes_controller.rb @@ -1,5 +1,4 @@ class JoinCodesController < ApplicationController - require_untenanted_access allow_unauthenticated_access before_action :set_join_code before_action :ensure_join_code_is_valid @@ -7,7 +6,7 @@ class JoinCodesController < ApplicationController layout "public" def new - @account_name = ApplicationRecord.with_tenant(tenant) { Account.sole.name } + @account_name = Current.account.name end def create @@ -30,7 +29,8 @@ class JoinCodesController < ApplicationController end def set_join_code - @join_code ||= ApplicationRecord.with_tenant(tenant) { Account::JoinCode.active.find_by(code: code) } + # TODO:PLANB: this find should be scoped by account + @join_code ||= Account::JoinCode.active.find_by(code: code) end def tenant diff --git a/app/controllers/memberships/email_addresses/confirmations_controller.rb b/app/controllers/memberships/email_addresses/confirmations_controller.rb index b53fdc135..42d9fb096 100644 --- a/app/controllers/memberships/email_addresses/confirmations_controller.rb +++ b/app/controllers/memberships/email_addresses/confirmations_controller.rb @@ -1,5 +1,4 @@ class Memberships::EmailAddresses::ConfirmationsController < ApplicationController - require_untenanted_access allow_unauthenticated_access before_action :set_membership diff --git a/app/controllers/memberships/email_addresses_controller.rb b/app/controllers/memberships/email_addresses_controller.rb index 0870410a9..f72389e64 100644 --- a/app/controllers/memberships/email_addresses_controller.rb +++ b/app/controllers/memberships/email_addresses_controller.rb @@ -1,6 +1,4 @@ class Memberships::EmailAddressesController < ApplicationController - require_untenanted_access - layout "public" before_action :set_membership diff --git a/app/controllers/memberships/unlink_controller.rb b/app/controllers/memberships/unlink_controller.rb index 63664aca8..ab29d5be9 100644 --- a/app/controllers/memberships/unlink_controller.rb +++ b/app/controllers/memberships/unlink_controller.rb @@ -1,5 +1,4 @@ class Memberships::UnlinkController < ApplicationController - require_untenanted_access before_action :set_membership def show diff --git a/app/controllers/pwa_controller.rb b/app/controllers/pwa_controller.rb index bd6127182..53f299311 100644 --- a/app/controllers/pwa_controller.rb +++ b/app/controllers/pwa_controller.rb @@ -1,5 +1,4 @@ class PwaController < ApplicationController - require_untenanted_access skip_forgery_protection # We need a stable URL at the root, so we can't use the regular asset path here. diff --git a/app/controllers/sessions/magic_links_controller.rb b/app/controllers/sessions/magic_links_controller.rb index 3b836e445..43021befc 100644 --- a/app/controllers/sessions/magic_links_controller.rb +++ b/app/controllers/sessions/magic_links_controller.rb @@ -1,5 +1,4 @@ class Sessions::MagicLinksController < ApplicationController - require_untenanted_access require_unauthenticated_access rate_limit to: 10, within: 15.minutes, only: :create, with: -> { redirect_to session_magic_link_path, alert: "Try again in 15 minutes." } diff --git a/app/controllers/sessions/menus_controller.rb b/app/controllers/sessions/menus_controller.rb index 7ad3b6a09..c6f884856 100644 --- a/app/controllers/sessions/menus_controller.rb +++ b/app/controllers/sessions/menus_controller.rb @@ -1,6 +1,4 @@ class Sessions::MenusController < ApplicationController - require_untenanted_access - before_action(if: :render_as_menu_section?) { request.variant = :menu_section } layout "public" diff --git a/app/controllers/sessions/transfers_controller.rb b/app/controllers/sessions/transfers_controller.rb index b3b7ae773..97a48daa4 100644 --- a/app/controllers/sessions/transfers_controller.rb +++ b/app/controllers/sessions/transfers_controller.rb @@ -1,5 +1,4 @@ class Sessions::TransfersController < ApplicationController - require_untenanted_access require_unauthenticated_access def show diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 8f8222ed9..e6f4ab665 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -4,7 +4,6 @@ class SessionsController < ApplicationController SIGNUP_PASSWORD = Rails.env.local? ? "testpassword" : Rails.application.credentials.account_signup_http_basic_auth.password http_basic_authenticate_with name: SIGNUP_USERNAME, password: SIGNUP_PASSWORD, realm: "Fizzy Signup", only: :create, unless: -> { Identity.exists?(email_address: email_address) } - require_untenanted_access require_unauthenticated_access except: :destroy rate_limit to: 10, within: 3.minutes, only: :create, with: -> { redirect_to new_session_path, alert: "Try again later." } diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index e4f5b4945..0839f1ae3 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,7 +1,7 @@ module ApplicationHelper def page_title_tag - account_name = if ApplicationRecord.current_tenant && Current.session&.identity&.memberships&.many? - Account.sole&.name + account_name = if Current.account && Current.session&.identity&.memberships&.many? + Current.account&.name end tag.title [ @page_title, account_name, "Fizzy" ].compact.join(" | ") end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index abeb3ae09..7fd915dba 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -7,8 +7,8 @@ class ApplicationMailer < ActionMailer::Base private def default_url_options - if ApplicationRecord.current_tenant - super.merge(script_name: Account.sole.slug) + if Current.account + super.merge(script_name: Current.account.slug) else super end diff --git a/app/models/board/entropic.rb b/app/models/board/entropic.rb index b29d5ea42..d49e0007a 100644 --- a/app/models/board/entropic.rb +++ b/app/models/board/entropic.rb @@ -7,7 +7,7 @@ module Board::Entropic end def entropy - super || Account.sole.entropy + super || Current.account.entropy end def auto_postpone_period=(new_value) diff --git a/app/models/card/entropic.rb b/app/models/card/entropic.rb index 478d4bc52..c666fa0e8 100644 --- a/app/models/card/entropic.rb +++ b/app/models/card/entropic.rb @@ -6,14 +6,14 @@ module Card::Entropic active .left_outer_joins(board: :entropy) .where("last_active_at <= DATETIME('now', '-' || COALESCE(entropies.auto_postpone_period, ?) || ' seconds')", - Account.sole.entropy.auto_postpone_period) + Current.account.entropy.auto_postpone_period) end scope :postponing_soon, -> do active .left_outer_joins(board: :entropy) - .where("last_active_at > DATETIME('now', '-' || COALESCE(entropies.auto_postpone_period, ?) || ' seconds')", Account.sole.entropy.auto_postpone_period) - .where("last_active_at <= DATETIME('now', '-' || CAST(COALESCE(entropies.auto_postpone_period, ?) * 0.75 AS INTEGER) || ' seconds')", Account.sole.entropy.auto_postpone_period) + .where("last_active_at > DATETIME('now', '-' || COALESCE(entropies.auto_postpone_period, ?) || ' seconds')", Current.account.entropy.auto_postpone_period) + .where("last_active_at <= DATETIME('now', '-' || CAST(COALESCE(entropies.auto_postpone_period, ?) * 0.75 AS INTEGER) || ' seconds')", Current.account.entropy.auto_postpone_period) end delegate :auto_postpone_period, to: :board diff --git a/app/models/card/promptable.rb b/app/models/card/promptable.rb index e30816023..ef2799b8f 100644 --- a/app/models/card/promptable.rb +++ b/app/models/card/promptable.rb @@ -24,7 +24,7 @@ module Card::Promptable * Board id: #{board_id} * Board name: #{board.name} * Number of comments: #{comments.count} - * Path: #{card_path(self, script_name: Account.sole.slug)} + * Path: #{card_path(self, script_name: Current.account.slug)} END OF CARD #{id} PROMPT diff --git a/app/models/comment/promptable.rb b/app/models/comment/promptable.rb index 93c931432..bc20ebfa3 100644 --- a/app/models/comment/promptable.rb +++ b/app/models/comment/promptable.rb @@ -20,7 +20,7 @@ module Comment::Promptable * Card title: #{card.title} * Created by: #{creator.name}} * Created at: #{created_at}} - * Path: #{card_path(card, anchor: ActionView::RecordIdentifier.dom_id(self), script_name: Account.sole.slug)} + * Path: #{card_path(card, anchor: ActionView::RecordIdentifier.dom_id(self), script_name: Current.account.slug)} END OF COMMENT #{id} PROMPT end diff --git a/app/models/current.rb b/app/models/current.rb index a3ba812a6..ad5131128 100644 --- a/app/models/current.rb +++ b/app/models/current.rb @@ -1,5 +1,5 @@ class Current < ActiveSupport::CurrentAttributes - attribute :session, :membership + attribute :session, :membership, :account attribute :http_method, :request_id, :user_agent, :ip_address, :referrer delegate :identity, to: :session, allow_nil: true @@ -8,8 +8,9 @@ class Current < ActiveSupport::CurrentAttributes def session=(value) super(value) - unless value.nil? - self.membership = identity.memberships.find_by(tenant: ApplicationRecord.current_tenant) - end + # # TODO:PLANB: not sure how to patch this up right now + # unless value.nil? + # self.membership = identity.memberships.find_by(tenant: ApplicationRecord.current_tenant) + # end end end diff --git a/app/models/membership.rb b/app/models/membership.rb index 30cd658c2..03fba7d63 100644 --- a/app/models/membership.rb +++ b/app/models/membership.rb @@ -16,14 +16,11 @@ class Membership < ApplicationRecord end def account_name - ApplicationRecord.with_tenant(tenant) { Account.sole.name } - rescue ActiveRecord::Tenanted::TenantDoesNotExistError, ActiveRecord::RecordNotFound - nil + Current.account.name end def user - ApplicationRecord.with_tenant(tenant) { User.find_by(membership_id: id) } - rescue ActiveRecord::Tenanted::TenantDoesNotExistError - nil + # TODO:PLANB: should this find should be scoped by account? + User.find_by(membership_id: id) end end diff --git a/app/models/notification_pusher.rb b/app/models/notification_pusher.rb index 0938954b0..debfc225b 100644 --- a/app/models/notification_pusher.rb +++ b/app/models/notification_pusher.rb @@ -88,7 +88,7 @@ class NotificationPusher { title: "New notification", body: "You have a new notification", - path: "#{Account.sole.slug}#{notifications_path}" + path: "#{Current.account.slug}#{notifications_path}" } end @@ -110,10 +110,10 @@ class NotificationPusher end def card_path(card) - "#{Account.sole.slug}#{Rails.application.routes.url_helpers.card_path(card)}" + "#{Current.account.slug}#{Rails.application.routes.url_helpers.card_path(card)}" end def card_path_with_comment_anchor(comment) - "#{Account.sole.slug}#{Rails.application.routes.url_helpers.card_path(comment.card, anchor: ActionView::RecordIdentifier.dom_id(comment))}" + "#{Current.account.slug}#{Rails.application.routes.url_helpers.card_path(comment.card, anchor: ActionView::RecordIdentifier.dom_id(comment))}" end end diff --git a/app/views/layouts/shared/_head.html.erb b/app/views/layouts/shared/_head.html.erb index fd4b7cab7..4569cd9fd 100644 --- a/app/views/layouts/shared/_head.html.erb +++ b/app/views/layouts/shared/_head.html.erb @@ -23,7 +23,7 @@ <%= yield :head %> - <% if ApplicationRecord.current_tenant %> + <% if Current.account %> <% end %> diff --git a/app/views/my/menus/show.html.erb b/app/views/my/menus/show.html.erb index 94e14a642..50b7089e4 100644 --- a/app/views/my/menus/show.html.erb +++ b/app/views/my/menus/show.html.erb @@ -61,7 +61,7 @@ <% end %> <% end %> - <%= turbo_frame_tag Current.identity, :account_menu, src: session_menu_url(script_name: nil, menu_section: true, without: ApplicationRecord.current_tenant) %> + <%= turbo_frame_tag Current.identity, :account_menu, src: session_menu_url(script_name: nil, menu_section: true, without: Current.account.id) %> <% end %>