From fc56ad9d7cfbb7d1d6770164d9a93ab8ca526dee Mon Sep 17 00:00:00 2001 From: Donal McBreen Date: Wed, 12 Nov 2025 16:42:34 +0000 Subject: [PATCH] Combine uuid-old and uuid branch changes - Switch to binary 16 for UUID keys - Remove AccountScopedRecord base class, all model use binary uuids now - Fix the search sql to serialize uuids properly - Patch the MySQL schema dumper to output binary lengths --- app/models/access.rb | 2 +- app/models/account.rb | 2 +- app/models/account/join_code.rb | 2 +- app/models/account_scoped_record.rb | 5 - app/models/application_record.rb | 2 + app/models/assignment.rb | 2 +- app/models/board.rb | 2 +- app/models/board/accessible.rb | 12 +- app/models/board/publication.rb | 2 +- app/models/card.rb | 2 +- app/models/card/activity_spike.rb | 2 +- app/models/card/engagement.rb | 2 +- app/models/card/goldness.rb | 2 +- app/models/card/not_now.rb | 2 +- app/models/card/searchable.rb | 5 +- app/models/closure.rb | 2 +- app/models/column.rb | 2 +- app/models/comment.rb | 2 +- app/models/concerns/searchable.rb | 17 +- app/models/concerns/uuid_primary_key.rb | 24 -- app/models/entropy.rb | 2 +- app/models/event.rb | 2 +- app/models/filter.rb | 2 +- app/models/mention.rb | 2 +- app/models/notification.rb | 2 +- app/models/notification/bundle.rb | 2 +- app/models/pin.rb | 2 +- app/models/push/subscription.rb | 2 +- app/models/reaction.rb | 2 +- app/models/search.rb | 4 +- app/models/search/query.rb | 2 +- app/models/search/result.rb | 5 +- app/models/step.rb | 2 +- app/models/tag.rb | 2 +- app/models/tagging.rb | 2 +- app/models/user.rb | 2 +- app/models/user/accessor.rb | 2 +- app/models/user/settings.rb | 2 +- app/models/watch.rb | 2 +- app/models/webhook.rb | 2 +- app/models/webhook/delinquency_tracker.rb | 2 +- app/models/webhook/delivery.rb | 2 +- config/application.rb | 5 + config/initializers/uuid_framework_models.rb | 8 +- config/initializers/uuid_primary_keys.rb | 75 ++++ db/migrate/20251111122540_initial_schema.rb | 248 ++++++------- .../20251112093037_create_search_indices.rb | 8 +- db/schema.rb | 347 +++++++++--------- lib/rails_ext/active_record_uuid_type.rb | 37 ++ script/import-sqlite-database.rb | 2 +- test/models/comment/searchable_test.rb | 26 +- test/test_helper.rb | 5 +- 52 files changed, 513 insertions(+), 390 deletions(-) delete mode 100644 app/models/account_scoped_record.rb delete mode 100644 app/models/concerns/uuid_primary_key.rb create mode 100644 config/initializers/uuid_primary_keys.rb create mode 100644 lib/rails_ext/active_record_uuid_type.rb diff --git a/app/models/access.rb b/app/models/access.rb index 8314cf87d..27026fb87 100644 --- a/app/models/access.rb +++ b/app/models/access.rb @@ -1,4 +1,4 @@ -class Access < AccountScopedRecord +class Access < ApplicationRecord belongs_to :board, touch: true belongs_to :user, touch: true diff --git a/app/models/account.rb b/app/models/account.rb index 279665aa8..46ba44213 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -1,4 +1,4 @@ -class Account < AccountScopedRecord +class Account < ApplicationRecord include Entropic, Seedeable has_one :join_code diff --git a/app/models/account/join_code.rb b/app/models/account/join_code.rb index 3e9efc277..49fe9af22 100644 --- a/app/models/account/join_code.rb +++ b/app/models/account/join_code.rb @@ -1,4 +1,4 @@ -class Account::JoinCode < AccountScopedRecord +class Account::JoinCode < ApplicationRecord CODE_LENGTH = 12 belongs_to :account diff --git a/app/models/account_scoped_record.rb b/app/models/account_scoped_record.rb deleted file mode 100644 index 9b060b01a..000000000 --- a/app/models/account_scoped_record.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AccountScopedRecord < ApplicationRecord - self.abstract_class = true - - include UuidPrimaryKey -end diff --git a/app/models/application_record.rb b/app/models/application_record.rb index f0814baa6..6fecd8d22 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -1,5 +1,7 @@ class ApplicationRecord < ActiveRecord::Base primary_abstract_class + attribute :id, :uuid, default: -> { ActiveRecord::Type::Uuid.generate } + connects_to database: { writing: :primary, reading: :replica } end diff --git a/app/models/assignment.rb b/app/models/assignment.rb index 21cb90386..312bc3bf7 100644 --- a/app/models/assignment.rb +++ b/app/models/assignment.rb @@ -1,4 +1,4 @@ -class Assignment < AccountScopedRecord +class Assignment < ApplicationRecord belongs_to :card, touch: true belongs_to :assignee, class_name: "User" diff --git a/app/models/board.rb b/app/models/board.rb index 238d6929a..4b5752604 100644 --- a/app/models/board.rb +++ b/app/models/board.rb @@ -1,4 +1,4 @@ -class Board < AccountScopedRecord +class Board < ApplicationRecord include Accessible, AutoPostponing, Broadcastable, Cards, Entropic, Filterable, Publishable, Triageable belongs_to :account, default: -> { Current.account } diff --git a/app/models/board/accessible.rb b/app/models/board/accessible.rb index 72166452c..1302438e5 100644 --- a/app/models/board/accessible.rb +++ b/app/models/board/accessible.rb @@ -11,7 +11,7 @@ module Board::Accessible end def grant_to(users) - Access.insert_all Array(users).collect { |user| { id: UuidPrimaryKey.generate, board_id: proxy_association.owner.id, user_id: user.id } } + Access.insert_all Array(users).collect { |user| { id: ActiveRecord::Type::Uuid.generate, board_id: proxy_association.owner.id, user_id: user.id } } end def revoke_from(users) @@ -66,11 +66,14 @@ module Board::Accessible # # 1. Mention->Card # 2. Mention->Comment->Card + uuid_type = ActiveRecord::Type.lookup(:uuid, adapter: :trilogy) + board_id_binary = uuid_type.serialize(id) + user.mentions .joins("LEFT JOIN cards ON mentions.source_id = cards.id AND mentions.source_type = 'Card'") .joins("LEFT JOIN comments ON mentions.source_id = comments.id AND mentions.source_type = 'Comment'") .joins("LEFT JOIN cards AS comment_cards ON comments.card_id = comment_cards.id") - .where("(mentions.source_type = 'Card' AND cards.board_id = ?) OR (mentions.source_type = 'Comment' AND comment_cards.board_id = ?)", id, id) + .where("(mentions.source_type = 'Card' AND cards.board_id = ?) OR (mentions.source_type = 'Comment' AND comment_cards.board_id = ?)", board_id_binary, board_id_binary) end def notifications_for_user(user) @@ -81,6 +84,9 @@ module Board::Accessible # # Notification->Event->Mention->Card and Notification->Event->Mention->Comment->Card are # handled by destroying mentions_for_user. + uuid_type = ActiveRecord::Type.lookup(:uuid, adapter: :trilogy) + board_id_binary = uuid_type.serialize(id) + user.notifications .joins("LEFT JOIN events ON notifications.source_id = events.id AND notifications.source_type = 'Event'") .joins("LEFT JOIN cards AS event_cards ON events.eventable_id = event_cards.id AND events.eventable_type = 'Card'") @@ -88,7 +94,7 @@ module Board::Accessible .joins("LEFT JOIN cards AS event_comment_cards ON event_comments.card_id = event_comment_cards.id") .where("(notifications.source_type = 'Event' AND events.eventable_type = 'Card' AND event_cards.board_id = ?) OR (notifications.source_type = 'Event' AND events.eventable_type = 'Comment' AND event_comment_cards.board_id = ?)", - id, id) + board_id_binary, board_id_binary) end def watches_for(user) diff --git a/app/models/board/publication.rb b/app/models/board/publication.rb index 7097dd34c..a21fb00b6 100644 --- a/app/models/board/publication.rb +++ b/app/models/board/publication.rb @@ -1,4 +1,4 @@ -class Board::Publication < AccountScopedRecord +class Board::Publication < ApplicationRecord belongs_to :board has_secure_token :key diff --git a/app/models/card.rb b/app/models/card.rb index ce3da1712..a509b4ff2 100644 --- a/app/models/card.rb +++ b/app/models/card.rb @@ -1,4 +1,4 @@ -class Card < AccountScopedRecord +class Card < ApplicationRecord include Assignable, Attachments, Broadcastable, Closeable, Colored, Entropic, Eventable, Golden, Mentions, Multistep, Pinnable, Postponable, Promptable, Readable, Searchable, Stallable, Statuses, Taggable, Triageable, Watchable diff --git a/app/models/card/activity_spike.rb b/app/models/card/activity_spike.rb index c1a2b6caf..cbfbe906a 100644 --- a/app/models/card/activity_spike.rb +++ b/app/models/card/activity_spike.rb @@ -1,3 +1,3 @@ -class Card::ActivitySpike < AccountScopedRecord +class Card::ActivitySpike < ApplicationRecord belongs_to :card, touch: true end diff --git a/app/models/card/engagement.rb b/app/models/card/engagement.rb index c2de46149..e9a46428f 100644 --- a/app/models/card/engagement.rb +++ b/app/models/card/engagement.rb @@ -1,4 +1,4 @@ -class Card::Engagement < AccountScopedRecord +class Card::Engagement < ApplicationRecord belongs_to :card, class_name: "::Card", touch: true validates :status, presence: true, inclusion: { in: %w[doing on_deck] } diff --git a/app/models/card/goldness.rb b/app/models/card/goldness.rb index bea6b75e9..fc2697328 100644 --- a/app/models/card/goldness.rb +++ b/app/models/card/goldness.rb @@ -1,3 +1,3 @@ -class Card::Goldness < AccountScopedRecord +class Card::Goldness < ApplicationRecord belongs_to :card, touch: true end diff --git a/app/models/card/not_now.rb b/app/models/card/not_now.rb index 89549e585..dcd16a86d 100644 --- a/app/models/card/not_now.rb +++ b/app/models/card/not_now.rb @@ -1,4 +1,4 @@ -class Card::NotNow < AccountScopedRecord +class Card::NotNow < ApplicationRecord belongs_to :card, class_name: "::Card", touch: true belongs_to :user, optional: true end diff --git a/app/models/card/searchable.rb b/app/models/card/searchable.rb index 713e92a3a..0c9831eac 100644 --- a/app/models/card/searchable.rb +++ b/app/models/card/searchable.rb @@ -9,8 +9,11 @@ module Card::Searchable if query.valid? table_name = Searchable.search_index_table_name(user.account_id) + uuid_type = ActiveRecord::Type.lookup(:uuid, adapter: :trilogy) + serialized_board_ids = user.board_ids.map { |id| uuid_type.serialize(id) } + joins("INNER JOIN #{table_name} ON #{table_name}.card_id = cards.id AND #{table_name}.board_id = cards.board_id") - .where("#{table_name}.board_id IN (?)", user.board_ids) + .where("#{table_name}.board_id IN (?)", serialized_board_ids) .where("MATCH(#{table_name}.content, #{table_name}.title) AGAINST(? IN BOOLEAN MODE)", query.to_s) .distinct else diff --git a/app/models/closure.rb b/app/models/closure.rb index 813e5a23d..ec0a812fc 100644 --- a/app/models/closure.rb +++ b/app/models/closure.rb @@ -1,4 +1,4 @@ -class Closure < AccountScopedRecord +class Closure < ApplicationRecord belongs_to :card, touch: true belongs_to :user, optional: true end diff --git a/app/models/column.rb b/app/models/column.rb index bca0efe8a..5b3815561 100644 --- a/app/models/column.rb +++ b/app/models/column.rb @@ -1,4 +1,4 @@ -class Column < AccountScopedRecord +class Column < ApplicationRecord include Colored, Positioned belongs_to :account, default: -> { Current.account } diff --git a/app/models/comment.rb b/app/models/comment.rb index e6be740a7..b5e84d692 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -1,4 +1,4 @@ -class Comment < AccountScopedRecord +class Comment < ApplicationRecord include Attachments, Eventable, Mentions, Promptable, Searchable belongs_to :account, default: -> { Current.account } diff --git a/app/models/concerns/searchable.rb b/app/models/concerns/searchable.rb index a4ae98531..0d0e11c7e 100644 --- a/app/models/concerns/searchable.rb +++ b/app/models/concerns/searchable.rb @@ -18,13 +18,14 @@ module Searchable private def create_in_search_index table_name = Searchable.search_index_table_name(account_id) + uuid_type = ActiveRecord::Type.lookup(:uuid, adapter: :trilogy) self.class.connection.execute self.class.sanitize_sql([ "INSERT INTO #{table_name} (searchable_type, searchable_id, card_id, board_id, title, content, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)", self.class.name, - id, - search_card_id, - search_board_id, + uuid_type.serialize(id), + uuid_type.serialize(search_card_id), + uuid_type.serialize(search_board_id), search_title, search_content, created_at @@ -33,16 +34,17 @@ module Searchable def update_in_search_index table_name = Searchable.search_index_table_name(account_id) + uuid_type = ActiveRecord::Type.lookup(:uuid, adapter: :trilogy) result = self.class.connection.execute(self.class.sanitize_sql([ "UPDATE #{table_name} SET card_id = ?, board_id = ?, title = ?, content = ?, created_at = ? WHERE searchable_type = ? AND searchable_id = ?", - search_card_id, - search_board_id, + uuid_type.serialize(search_card_id), + uuid_type.serialize(search_board_id), search_title, search_content, created_at, self.class.name, - id + uuid_type.serialize(id) ])) create_in_search_index if result.affected_rows == 0 @@ -50,11 +52,12 @@ module Searchable def remove_from_search_index table_name = Searchable.search_index_table_name(account_id) + uuid_type = ActiveRecord::Type.lookup(:uuid, adapter: :trilogy) self.class.connection.execute self.class.sanitize_sql([ "DELETE FROM #{table_name} WHERE searchable_type = ? AND searchable_id = ?", self.class.name, - id + uuid_type.serialize(id) ]) end diff --git a/app/models/concerns/uuid_primary_key.rb b/app/models/concerns/uuid_primary_key.rb deleted file mode 100644 index c905263ef..000000000 --- a/app/models/concerns/uuid_primary_key.rb +++ /dev/null @@ -1,24 +0,0 @@ -module UuidPrimaryKey - extend ActiveSupport::Concern - - included do - before_create :generate_uuid_primary_key - end - - def self.generate - # Base 36 UUIDs are a bit more compact; a bit less ugly - uuid_to_base36(SecureRandom.uuid_v7) - end - - def self.uuid_to_base36(uuid) - # Convert standard UUID format to base36 (lowercase), padded to 25 chars - uuid.delete('-').to_i(16).to_s(36).rjust(25, '0') - end - - private - - def generate_uuid_primary_key - return if id.present? - self.id = UuidPrimaryKey.generate - end -end diff --git a/app/models/entropy.rb b/app/models/entropy.rb index 8dfe0fef9..6be6675ee 100644 --- a/app/models/entropy.rb +++ b/app/models/entropy.rb @@ -1,4 +1,4 @@ -class Entropy < AccountScopedRecord +class Entropy < ApplicationRecord belongs_to :container, polymorphic: true after_commit -> { container.cards.touch_all } diff --git a/app/models/event.rb b/app/models/event.rb index 78cd3c020..cca00d66f 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -1,4 +1,4 @@ -class Event < AccountScopedRecord +class Event < ApplicationRecord include Notifiable, Particulars, Promptable belongs_to :account, default: -> { Current.account } diff --git a/app/models/filter.rb b/app/models/filter.rb index d3ab61397..894a7ccca 100644 --- a/app/models/filter.rb +++ b/app/models/filter.rb @@ -1,4 +1,4 @@ -class Filter < AccountScopedRecord +class Filter < ApplicationRecord include Fields, Params, Resources, Summarized belongs_to :account, default: -> { Current.account } diff --git a/app/models/mention.rb b/app/models/mention.rb index 9a59359d4..54a20e0ce 100644 --- a/app/models/mention.rb +++ b/app/models/mention.rb @@ -1,4 +1,4 @@ -class Mention < AccountScopedRecord +class Mention < ApplicationRecord include Notifiable belongs_to :source, polymorphic: true diff --git a/app/models/notification.rb b/app/models/notification.rb index 6bc170a27..8e6010acd 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -1,4 +1,4 @@ -class Notification < AccountScopedRecord +class Notification < ApplicationRecord include PushNotifiable belongs_to :account, default: -> { user.account } diff --git a/app/models/notification/bundle.rb b/app/models/notification/bundle.rb index 78fd4eece..a4d87e72d 100644 --- a/app/models/notification/bundle.rb +++ b/app/models/notification/bundle.rb @@ -1,4 +1,4 @@ -class Notification::Bundle < AccountScopedRecord +class Notification::Bundle < ApplicationRecord belongs_to :account, default: -> { user.account } belongs_to :user diff --git a/app/models/pin.rb b/app/models/pin.rb index e5710a7d8..425a504bf 100644 --- a/app/models/pin.rb +++ b/app/models/pin.rb @@ -1,4 +1,4 @@ -class Pin < AccountScopedRecord +class Pin < ApplicationRecord belongs_to :card belongs_to :user diff --git a/app/models/push/subscription.rb b/app/models/push/subscription.rb index 492b5552a..5776ef25f 100644 --- a/app/models/push/subscription.rb +++ b/app/models/push/subscription.rb @@ -1,4 +1,4 @@ -class Push::Subscription < AccountScopedRecord +class Push::Subscription < ApplicationRecord belongs_to :account, default: -> { Current.account } belongs_to :user diff --git a/app/models/reaction.rb b/app/models/reaction.rb index 15f60f4ea..d70f8333b 100644 --- a/app/models/reaction.rb +++ b/app/models/reaction.rb @@ -1,4 +1,4 @@ -class Reaction < AccountScopedRecord +class Reaction < ApplicationRecord belongs_to :comment, touch: true belongs_to :reacter, class_name: "User", default: -> { Current.user } diff --git a/app/models/search.rb b/app/models/search.rb index df6c37841..71c739bbf 100644 --- a/app/models/search.rb +++ b/app/models/search.rb @@ -27,11 +27,13 @@ class Search query_string = query.to_s sanitized_raw_query = Search::Result.connection.quote(query.terms) table_name = Searchable.search_index_table_name(user.account_id) + uuid_type = ActiveRecord::Type.lookup(:uuid, adapter: :trilogy) + serialized_board_ids = board_ids.map { |id| uuid_type.serialize(id) } Search::Result.from(table_name) .joins("INNER JOIN cards ON #{table_name}.card_id = cards.id") .joins("INNER JOIN boards ON cards.board_id = boards.id") - .where("#{table_name}.board_id IN (?)", board_ids) + .where("#{table_name}.board_id IN (?)", serialized_board_ids) .where("MATCH(#{table_name}.content, #{table_name}.title) AGAINST(? IN BOOLEAN MODE)", query_string) .select([ "#{table_name}.card_id as card_id", diff --git a/app/models/search/query.rb b/app/models/search/query.rb index 2c5d8ccf3..0186cc424 100644 --- a/app/models/search/query.rb +++ b/app/models/search/query.rb @@ -1,4 +1,4 @@ -class Search::Query < AccountScopedRecord +class Search::Query < ApplicationRecord validates :terms, presence: true before_validation :sanitize_terms diff --git a/app/models/search/result.rb b/app/models/search/result.rb index 10eed5f89..1d0a4c9be 100644 --- a/app/models/search/result.rb +++ b/app/models/search/result.rb @@ -1,4 +1,7 @@ -class Search::Result < AccountScopedRecord +class Search::Result < ApplicationRecord + attribute :card_id, :uuid + attribute :comment_id, :uuid + belongs_to :creator, class_name: "User" belongs_to :card, foreign_key: :card_id, optional: true belongs_to :comment, foreign_key: :comment_id, optional: true diff --git a/app/models/step.rb b/app/models/step.rb index 51d5be20d..c3cf12378 100644 --- a/app/models/step.rb +++ b/app/models/step.rb @@ -1,4 +1,4 @@ -class Step < AccountScopedRecord +class Step < ApplicationRecord belongs_to :account, default: -> { Current.account } belongs_to :card, touch: true diff --git a/app/models/tag.rb b/app/models/tag.rb index 68b48667d..192f0b86e 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -1,4 +1,4 @@ -class Tag < AccountScopedRecord +class Tag < ApplicationRecord include Attachable, Filterable belongs_to :account, default: -> { Current.account } diff --git a/app/models/tagging.rb b/app/models/tagging.rb index d05487c44..73daa0879 100644 --- a/app/models/tagging.rb +++ b/app/models/tagging.rb @@ -1,4 +1,4 @@ -class Tagging < AccountScopedRecord +class Tagging < ApplicationRecord belongs_to :tag belongs_to :card, touch: true end diff --git a/app/models/user.rb b/app/models/user.rb index aa49efb97..b940b67a0 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,4 +1,4 @@ -class User < AccountScopedRecord +class User < ApplicationRecord include Accessor, Assignee, Attachable, Configurable, Mentionable, Named, Notifiable, Role, Searcher, Watcher include Timelined # Depends on Accessor diff --git a/app/models/user/accessor.rb b/app/models/user/accessor.rb index e2047cb90..1c7529b8e 100644 --- a/app/models/user/accessor.rb +++ b/app/models/user/accessor.rb @@ -12,6 +12,6 @@ module User::Accessor private def grant_access_to_boards - Access.insert_all account.boards.all_access.pluck(:id).collect { |board_id| { id: UuidPrimaryKey.generate, board_id: board_id, user_id: id } } + Access.insert_all account.boards.all_access.pluck(:id).collect { |board_id| { id: ActiveRecord::Type::Uuid.generate, board_id: board_id, user_id: id } } end end diff --git a/app/models/user/settings.rb b/app/models/user/settings.rb index 8f43e401d..a381acae3 100644 --- a/app/models/user/settings.rb +++ b/app/models/user/settings.rb @@ -1,4 +1,4 @@ -class User::Settings < AccountScopedRecord +class User::Settings < ApplicationRecord belongs_to :user enum :bundle_email_frequency, %i[ never every_few_hours daily weekly ], diff --git a/app/models/watch.rb b/app/models/watch.rb index 965c57a18..7b88a7ec4 100644 --- a/app/models/watch.rb +++ b/app/models/watch.rb @@ -1,4 +1,4 @@ -class Watch < AccountScopedRecord +class Watch < ApplicationRecord belongs_to :user belongs_to :card, touch: true diff --git a/app/models/webhook.rb b/app/models/webhook.rb index 95eda26f1..c8292a1f6 100644 --- a/app/models/webhook.rb +++ b/app/models/webhook.rb @@ -1,4 +1,4 @@ -class Webhook < AccountScopedRecord +class Webhook < ApplicationRecord include Triggerable SLACK_WEBHOOK_URL_REGEX = %r{//hooks\.slack\.com/services/T[^\/]+/B[^\/]+/[^\/]+\Z}i diff --git a/app/models/webhook/delinquency_tracker.rb b/app/models/webhook/delinquency_tracker.rb index ff8ecaea0..1824db418 100644 --- a/app/models/webhook/delinquency_tracker.rb +++ b/app/models/webhook/delinquency_tracker.rb @@ -1,4 +1,4 @@ -class Webhook::DelinquencyTracker < AccountScopedRecord +class Webhook::DelinquencyTracker < ApplicationRecord DELINQUENCY_THRESHOLD = 10 DELINQUENCY_DURATION = 1.hour diff --git a/app/models/webhook/delivery.rb b/app/models/webhook/delivery.rb index d3c37d631..e90a8decb 100644 --- a/app/models/webhook/delivery.rb +++ b/app/models/webhook/delivery.rb @@ -1,4 +1,4 @@ -class Webhook::Delivery < AccountScopedRecord +class Webhook::Delivery < ApplicationRecord STALE_TRESHOLD = 7.days USER_AGENT = "fizzy/1.0.0 Webhook" ENDPOINT_TIMEOUT = 7.seconds diff --git a/config/application.rb b/config/application.rb index b9d7d031c..4a6f1f378 100644 --- a/config/application.rb +++ b/config/application.rb @@ -36,5 +36,10 @@ module Fizzy config.after_initialize do Rails.event.debug_mode = true end + + # Use UUID primary keys for all new tables + config.generators do |g| + g.orm :active_record, primary_key_type: :uuid + end end end diff --git a/config/initializers/uuid_framework_models.rb b/config/initializers/uuid_framework_models.rb index f6ffb3891..68a6b9066 100644 --- a/config/initializers/uuid_framework_models.rb +++ b/config/initializers/uuid_framework_models.rb @@ -1,7 +1,7 @@ # Inject UUID primary key support into Rails framework models Rails.application.config.to_prepare do - ActionText::RichText.include UuidPrimaryKey - ActiveStorage::Attachment.include UuidPrimaryKey - ActiveStorage::Blob.include UuidPrimaryKey - ActiveStorage::VariantRecord.include UuidPrimaryKey + ActionText::RichText.attribute :id, :uuid, default: -> { ActiveRecord::Type::Uuid.generate } + ActiveStorage::Attachment.attribute :id, :uuid, default: -> { ActiveRecord::Type::Uuid.generate } + ActiveStorage::Blob.attribute :id, :uuid, default: -> { ActiveRecord::Type::Uuid.generate } + ActiveStorage::VariantRecord.attribute :id, :uuid, default: -> { ActiveRecord::Type::Uuid.generate } end diff --git a/config/initializers/uuid_primary_keys.rb b/config/initializers/uuid_primary_keys.rb new file mode 100644 index 000000000..fb60c737f --- /dev/null +++ b/config/initializers/uuid_primary_keys.rb @@ -0,0 +1,75 @@ +# Automatically use UUID type for all binary(16) columns +ActiveSupport.on_load(:active_record) do + ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter.class_eval do + def lookup_cast_type(sql_type) + if sql_type == "varbinary(16)" + ActiveRecord::Type.lookup(:uuid, adapter: :trilogy) + else + super + end + end + + # Override quote to handle binary UUID values properly + def quote(value) + if value.is_a?(String) && value.encoding == Encoding::BINARY && value.bytesize == 16 + # Quote binary UUID as hex literal for MySQL + "X'#{value.unpack1('H*')}'" + else + super + end + end + end + + # Fix schema dumper to include limit for binary columns + module SchemaDumperBinaryLimit + def prepare_column_options(column) + spec = super + # Ensure binary columns with limits always include them in schema + if column.type == :binary && column.sql_type =~ /varbinary\((\d+)\)/ + spec[:limit] = $1.to_i + end + spec + end + end + + ActiveRecord::ConnectionAdapters::MySQL::SchemaDumper.prepend(SchemaDumperBinaryLimit) +end + +# Automatically convert :uuid to binary(16) for primary keys and columns +module ActiveRecord + module ConnectionAdapters + class TableDefinition + alias_method :original_set_primary_key, :set_primary_key + + def set_primary_key(table_name, id, primary_key, **options) + # Convert :uuid to :binary with limit 16 + if id == :uuid + id = :binary + options[:limit] = 16 + elsif id.is_a?(Hash) && id[:type] == :uuid + id[:type] = :binary + id[:limit] = 16 + end + + original_set_primary_key(table_name, id, primary_key, **options) + end + + alias_method :original_column, :column + + def column(name, type, **options) + # Convert :uuid to :binary with limit 16 for regular columns too + if type == :uuid + type = :binary + options[:limit] = 16 + end + + original_column(name, type, **options) + end + + # Define uuid as a column type method (like string, integer, etc.) + def uuid(name, **options) + column(name, :uuid, **options) + end + end + end +end diff --git a/db/migrate/20251111122540_initial_schema.rb b/db/migrate/20251111122540_initial_schema.rb index 390723fe1..4b736f712 100644 --- a/db/migrate/20251111122540_initial_schema.rb +++ b/db/migrate/20251111122540_initial_schema.rb @@ -1,20 +1,20 @@ class InitialSchema < ActiveRecord::Migration[8.2] def change - create_table "accesses", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + create_table "accesses", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| t.datetime "accessed_at" - t.string "board_id", limit: 25, null: false + t.uuid "board_id", null: false t.datetime "created_at", null: false t.string "involvement", default: "access_only", null: false t.datetime "updated_at", null: false - t.string "user_id", limit: 25, null: false + t.uuid "user_id", null: false t.index ["accessed_at"], name: "index_accesses_on_accessed_at", order: :desc t.index ["board_id", "user_id"], name: "index_accesses_on_board_id_and_user_id", unique: true t.index ["board_id"], name: "index_accesses_on_board_id" t.index ["user_id"], name: "index_accesses_on_user_id" end - create_table "account_join_codes", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "account_id", limit: 25 + create_table "account_join_codes", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + t.uuid "account_id" t.string "code", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false @@ -23,7 +23,7 @@ class InitialSchema < ActiveRecord::Migration[8.2] t.index ["code"], name: "index_account_join_codes_on_code", unique: true end - create_table "accounts", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + create_table "accounts", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| t.datetime "created_at", null: false t.integer "external_account_id" t.string "name", null: false @@ -31,27 +31,27 @@ class InitialSchema < ActiveRecord::Migration[8.2] t.index ["external_account_id"], name: "index_accounts_on_external_account_id", unique: true end - create_table "action_text_rich_texts", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + create_table "action_text_rich_texts", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| t.text "body", size: :long t.datetime "created_at", null: false t.string "name", null: false - t.string "record_id", limit: 25, null: false + t.uuid "record_id", null: false t.string "record_type", null: false t.datetime "updated_at", null: false t.index ["record_type", "record_id", "name"], name: "index_action_text_rich_texts_uniqueness", unique: true end - create_table "active_storage_attachments", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "blob_id", limit: 25, null: false + create_table "active_storage_attachments", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + t.uuid "blob_id", null: false t.datetime "created_at", null: false t.string "name", null: false - t.string "record_id", limit: 25, null: false + t.uuid "record_id", null: false t.string "record_type", null: false t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id" t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true end - create_table "active_storage_blobs", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + create_table "active_storage_blobs", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| t.bigint "byte_size", null: false t.string "checksum" t.string "content_type" @@ -63,38 +63,38 @@ class InitialSchema < ActiveRecord::Migration[8.2] t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true end - create_table "active_storage_variant_records", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "blob_id", limit: 25, null: false + create_table "active_storage_variant_records", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + t.uuid "blob_id", null: false t.string "variation_digest", null: false t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true end create_table "assignees_filters", id: false, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "assignee_id", limit: 25, null: false - t.string "filter_id", limit: 25, null: false + t.uuid "assignee_id", null: false + t.uuid "filter_id", null: false t.index ["assignee_id"], name: "index_assignees_filters_on_assignee_id" t.index ["filter_id"], name: "index_assignees_filters_on_filter_id" end create_table "assigners_filters", id: false, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "assigner_id", limit: 25, null: false - t.string "filter_id", limit: 25, null: false + t.uuid "assigner_id", null: false + t.uuid "filter_id", null: false t.index ["assigner_id"], name: "index_assigners_filters_on_assigner_id" t.index ["filter_id"], name: "index_assigners_filters_on_filter_id" end - create_table "assignments", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "assignee_id", limit: 25, null: false - t.string "assigner_id", limit: 25, null: false - t.string "card_id", limit: 25, null: false + create_table "assignments", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + t.uuid "assignee_id", null: false + t.uuid "assigner_id", null: false + t.uuid "card_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["assignee_id", "card_id"], name: "index_assignments_on_assignee_id_and_card_id", unique: true t.index ["card_id"], name: "index_assignments_on_card_id" end - create_table "board_publications", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "board_id", limit: 25, null: false + create_table "board_publications", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + t.uuid "board_id", null: false t.datetime "created_at", null: false t.string "key" t.datetime "updated_at", null: false @@ -102,32 +102,32 @@ class InitialSchema < ActiveRecord::Migration[8.2] t.index ["key"], name: "index_board_publications_on_key", unique: true end - create_table "boards", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "account_id", limit: 25 + create_table "boards", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + t.uuid "account_id" t.boolean "all_access", default: false, null: false t.datetime "created_at", null: false - t.string "creator_id", limit: 25, null: false + t.uuid "creator_id", null: false t.string "name", null: false t.datetime "updated_at", null: false t.index ["creator_id"], name: "index_boards_on_creator_id" end create_table "boards_filters", id: false, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "board_id", limit: 25, null: false - t.string "filter_id", limit: 25, null: false + t.uuid "board_id", null: false + t.uuid "filter_id", null: false t.index ["board_id"], name: "index_boards_filters_on_board_id" t.index ["filter_id"], name: "index_boards_filters_on_filter_id" end - create_table "card_activity_spikes", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "card_id", limit: 25, null: false + create_table "card_activity_spikes", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + t.uuid "card_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["card_id"], name: "index_card_activity_spikes_on_card_id" end - create_table "card_engagements", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "card_id", limit: 25 + create_table "card_engagements", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + t.uuid "card_id" t.datetime "created_at", null: false t.string "status", default: "doing", null: false t.datetime "updated_at", null: false @@ -135,28 +135,28 @@ class InitialSchema < ActiveRecord::Migration[8.2] t.index ["status"], name: "index_card_engagements_on_status" end - create_table "card_goldnesses", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "card_id", limit: 25, null: false + create_table "card_goldnesses", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + t.uuid "card_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["card_id"], name: "index_card_goldnesses_on_card_id", unique: true end - create_table "card_not_nows", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "card_id", limit: 25, null: false + create_table "card_not_nows", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + t.uuid "card_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.string "user_id", limit: 25 + t.uuid "user_id" t.index ["card_id"], name: "index_card_not_nows_on_card_id", unique: true t.index ["user_id"], name: "index_card_not_nows_on_user_id" end - create_table "cards", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "account_id", limit: 25 - t.string "board_id", limit: 25, null: false - t.string "column_id", limit: 25 + create_table "cards", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + t.uuid "account_id" + t.uuid "board_id", null: false + t.uuid "column_id" t.datetime "created_at", null: false - t.string "creator_id", limit: 25, null: false + t.uuid "creator_id", null: false t.date "due_on" t.datetime "last_active_at", null: false t.string "status", default: "drafted", null: false @@ -168,25 +168,25 @@ class InitialSchema < ActiveRecord::Migration[8.2] end create_table "closers_filters", id: false, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "closer_id", limit: 25, null: false - t.string "filter_id", limit: 25, null: false + t.uuid "closer_id", null: false + t.uuid "filter_id", null: false t.index ["closer_id"], name: "index_closers_filters_on_closer_id" t.index ["filter_id"], name: "index_closers_filters_on_filter_id" end - create_table "closures", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "card_id", limit: 25, null: false + create_table "closures", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + t.uuid "card_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.string "user_id", limit: 25 + t.uuid "user_id" t.index ["card_id", "created_at"], name: "index_closures_on_card_id_and_created_at" t.index ["card_id"], name: "index_closures_on_card_id", unique: true t.index ["user_id"], name: "index_closures_on_user_id" end - create_table "columns", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "account_id", limit: 25 - t.string "board_id", limit: 25, null: false + create_table "columns", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + t.uuid "account_id" + t.uuid "board_id", null: false t.string "color", null: false t.datetime "created_at", null: false t.string "name", null: false @@ -196,25 +196,25 @@ class InitialSchema < ActiveRecord::Migration[8.2] t.index ["board_id"], name: "index_columns_on_board_id" end - create_table "comments", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "account_id", limit: 25 - t.string "card_id", limit: 25, null: false + create_table "comments", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + t.uuid "account_id" + t.uuid "card_id", null: false t.datetime "created_at", null: false - t.string "creator_id", limit: 25, null: false + t.uuid "creator_id", null: false t.datetime "updated_at", null: false t.index ["card_id"], name: "index_comments_on_card_id" end create_table "creators_filters", id: false, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "creator_id", limit: 25, null: false - t.string "filter_id", limit: 25, null: false + t.uuid "creator_id", null: false + t.uuid "filter_id", null: false t.index ["creator_id"], name: "index_creators_filters_on_creator_id" t.index ["filter_id"], name: "index_creators_filters_on_filter_id" end - create_table "entropies", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + create_table "entropies", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| t.bigint "auto_postpone_period", default: 2592000, null: false - t.string "container_id", limit: 25, null: false + t.uuid "container_id", null: false t.string "container_type", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false @@ -222,13 +222,13 @@ class InitialSchema < ActiveRecord::Migration[8.2] t.index ["container_type", "container_id"], name: "index_entropy_configurations_on_container", unique: true end - create_table "events", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "account_id", limit: 25 + create_table "events", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + t.uuid "account_id" t.string "action", null: false - t.string "board_id", limit: 25, null: false + t.uuid "board_id", null: false t.datetime "created_at", null: false - t.string "creator_id", limit: 25, null: false - t.string "eventable_id", limit: 25, null: false + t.uuid "creator_id", null: false + t.uuid "eventable_id", null: false t.string "eventable_type", null: false t.json "particulars", default: -> { "(json_object())" } t.datetime "updated_at", null: false @@ -239,10 +239,10 @@ class InitialSchema < ActiveRecord::Migration[8.2] t.index ["eventable_type", "eventable_id"], name: "index_events_on_eventable" end - create_table "filters", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "account_id", limit: 25 + create_table "filters", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + t.uuid "account_id" t.datetime "created_at", null: false - t.string "creator_id", limit: 25, null: false + t.uuid "creator_id", null: false t.json "fields", default: -> { "(json_object())" }, null: false t.string "params_digest", null: false t.datetime "updated_at", null: false @@ -250,33 +250,33 @@ class InitialSchema < ActiveRecord::Migration[8.2] end create_table "filters_tags", id: false, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "filter_id", limit: 25, null: false - t.string "tag_id", limit: 25, null: false + t.uuid "filter_id", null: false + t.uuid "tag_id", null: false t.index ["filter_id"], name: "index_filters_tags_on_filter_id" t.index ["tag_id"], name: "index_filters_tags_on_tag_id" end - create_table "identities", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + create_table "identities", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" 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", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + create_table "magic_links", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| t.string "code", null: false t.datetime "created_at", null: false t.datetime "expires_at", null: false - t.bigint "identity_id" + t.uuid "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", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + create_table "memberships", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| t.datetime "created_at", null: false - t.bigint "identity_id", null: false + t.uuid "identity_id", null: false t.string "join_code" t.string "tenant", null: false t.datetime "updated_at", null: false @@ -285,11 +285,11 @@ class InitialSchema < ActiveRecord::Migration[8.2] t.index ["tenant"], name: "index_memberships_on_user_tenant_and_user_id" end - create_table "mentions", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + create_table "mentions", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| t.datetime "created_at", null: false - t.string "mentionee_id", limit: 25, null: false - t.string "mentioner_id", limit: 25, null: false - t.string "source_id", limit: 25, null: false + t.uuid "mentionee_id", null: false + t.uuid "mentioner_id", null: false + t.uuid "source_id", null: false t.string "source_type", null: false t.datetime "updated_at", null: false t.index ["mentionee_id"], name: "index_mentions_on_mentionee_id" @@ -297,53 +297,53 @@ class InitialSchema < ActiveRecord::Migration[8.2] t.index ["source_type", "source_id"], name: "index_mentions_on_source" end - create_table "notification_bundles", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "account_id", limit: 25 + create_table "notification_bundles", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + t.uuid "account_id" t.datetime "created_at", null: false t.datetime "ends_at", null: false t.datetime "starts_at", null: false t.integer "status", default: 0, null: false t.datetime "updated_at", null: false - t.string "user_id", limit: 25, null: false + t.uuid "user_id", null: false t.index ["ends_at", "status"], name: "index_notification_bundles_on_ends_at_and_status" t.index ["user_id", "starts_at", "ends_at"], name: "idx_on_user_id_starts_at_ends_at_7eae5d3ac5" t.index ["user_id", "status"], name: "index_notification_bundles_on_user_id_and_status" end - create_table "notifications", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "account_id", limit: 25 + create_table "notifications", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + t.uuid "account_id" t.datetime "created_at", null: false - t.string "creator_id", limit: 25 + t.uuid "creator_id" t.datetime "read_at" - t.string "source_id", limit: 25, null: false + t.uuid "source_id", null: false t.string "source_type", null: false t.datetime "updated_at", null: false - t.string "user_id", limit: 25, null: false + t.uuid "user_id", null: false t.index ["creator_id"], name: "index_notifications_on_creator_id" t.index ["source_type", "source_id"], name: "index_notifications_on_source" t.index ["user_id", "read_at", "created_at"], name: "index_notifications_on_user_id_and_read_at_and_created_at", order: { read_at: :desc, created_at: :desc } t.index ["user_id"], name: "index_notifications_on_user_id" end - create_table "pins", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "card_id", limit: 25, null: false + create_table "pins", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + t.uuid "card_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.string "user_id", limit: 25, null: false + t.uuid "user_id", null: false t.index ["card_id", "user_id"], name: "index_pins_on_card_id_and_user_id", unique: true t.index ["card_id"], name: "index_pins_on_card_id" t.index ["user_id"], name: "index_pins_on_user_id" end - create_table "push_subscriptions", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "account_id", limit: 25 + create_table "push_subscriptions", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + t.uuid "account_id" t.string "auth_key" t.datetime "created_at", null: false t.string "endpoint" t.string "p256dh_key" t.datetime "updated_at", null: false t.string "user_agent" - t.string "user_id", limit: 25, null: false + t.uuid "user_id", null: false t.index ["endpoint", "p256dh_key", "auth_key"], name: "idx_on_endpoint_p256dh_key_auth_key_7553014576" t.index ["endpoint"], name: "index_push_subscriptions_on_endpoint" t.index ["user_agent"], name: "index_push_subscriptions_on_user_agent" @@ -351,43 +351,43 @@ class InitialSchema < ActiveRecord::Migration[8.2] t.index ["user_id"], name: "index_push_subscriptions_on_user_id" end - create_table "reactions", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "comment_id", limit: 25, null: false + create_table "reactions", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + t.uuid "comment_id", null: false t.string "content", limit: 16, null: false t.datetime "created_at", null: false - t.string "reacter_id", limit: 25, null: false + t.uuid "reacter_id", null: false t.datetime "updated_at", null: false t.index ["comment_id"], name: "index_reactions_on_comment_id" t.index ["reacter_id"], name: "index_reactions_on_reacter_id" end - create_table "search_queries", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + create_table "search_queries", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| t.datetime "created_at", null: false t.string "terms", limit: 2000, null: false t.datetime "updated_at", null: false - t.string "user_id", limit: 25, null: false + t.uuid "user_id", null: false t.index ["user_id", "terms"], name: "index_search_queries_on_user_id_and_terms", length: { terms: 255 } t.index ["user_id", "updated_at"], name: "index_search_queries_on_user_id_and_updated_at", unique: true t.index ["user_id"], name: "index_search_queries_on_user_id" end - create_table "search_results", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + create_table "search_results", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| t.datetime "created_at", null: false t.datetime "updated_at", null: false end - create_table "sessions", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + create_table "sessions", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| t.datetime "created_at", null: false - t.bigint "identity_id", null: false + t.uuid "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 - create_table "steps", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "account_id", limit: 25 - t.string "card_id", limit: 25, null: false + create_table "steps", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + t.uuid "account_id" + t.uuid "card_id", null: false t.boolean "completed", default: false, null: false t.text "content", null: false t.datetime "created_at", null: false @@ -396,38 +396,38 @@ class InitialSchema < ActiveRecord::Migration[8.2] t.index ["card_id"], name: "index_steps_on_card_id" end - create_table "taggings", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "card_id", limit: 25, null: false + create_table "taggings", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + t.uuid "card_id", null: false t.datetime "created_at", null: false - t.string "tag_id", limit: 25, null: false + t.uuid "tag_id", null: false t.datetime "updated_at", null: false t.index ["card_id", "tag_id"], name: "index_taggings_on_card_id_and_tag_id", unique: true t.index ["tag_id"], name: "index_taggings_on_tag_id" end - create_table "tags", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "account_id", limit: 25 + create_table "tags", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + t.uuid "account_id" t.datetime "created_at", null: false t.string "title" t.datetime "updated_at", null: false t.index ["title"], name: "index_tags_on_title", unique: true end - create_table "user_settings", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + create_table "user_settings", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| t.integer "bundle_email_frequency", default: 0, null: false t.datetime "created_at", null: false t.string "timezone_name" t.datetime "updated_at", null: false - t.string "user_id", limit: 25, null: false + t.uuid "user_id", null: false t.index ["user_id", "bundle_email_frequency"], name: "index_user_settings_on_user_id_and_bundle_email_frequency" t.index ["user_id"], name: "index_user_settings_on_user_id" end - create_table "users", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "account_id", limit: 25 + create_table "users", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + t.uuid "account_id" t.boolean "active", default: true, null: false t.datetime "created_at", null: false - t.integer "membership_id" + t.uuid "membership_id" t.string "name", null: false t.string "role", default: "member", null: false t.datetime "updated_at", null: false @@ -435,42 +435,42 @@ class InitialSchema < ActiveRecord::Migration[8.2] t.index ["role"], name: "index_users_on_role" end - create_table "watches", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "card_id", limit: 25, null: false + create_table "watches", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + t.uuid "card_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.string "user_id", limit: 25, null: false + t.uuid "user_id", null: false t.boolean "watching", default: true, null: false t.index ["card_id"], name: "index_watches_on_card_id" t.index ["user_id", "card_id"], name: "index_watches_on_user_id_and_card_id" t.index ["user_id"], name: "index_watches_on_user_id" end - create_table "webhook_delinquency_trackers", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + create_table "webhook_delinquency_trackers", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| t.integer "consecutive_failures_count", default: 0 t.datetime "created_at", null: false t.datetime "first_failure_at" t.datetime "updated_at", null: false - t.string "webhook_id", limit: 25, null: false + t.uuid "webhook_id", null: false t.index ["webhook_id"], name: "index_webhook_delinquency_trackers_on_webhook_id" end - create_table "webhook_deliveries", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + create_table "webhook_deliveries", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| t.datetime "created_at", null: false - t.string "event_id", limit: 25, null: false + t.uuid "event_id", null: false t.text "request" t.text "response" t.string "state", null: false t.datetime "updated_at", null: false - t.string "webhook_id", limit: 25, null: false + t.uuid "webhook_id", null: false t.index ["event_id"], name: "index_webhook_deliveries_on_event_id" t.index ["webhook_id"], name: "index_webhook_deliveries_on_webhook_id" end - create_table "webhooks", id: :string, limit: 25, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| - t.string "account_id", limit: 25 + create_table "webhooks", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| + t.uuid "account_id" t.boolean "active", default: true, null: false - t.string "board_id", limit: 25, null: false + t.uuid "board_id", null: false t.datetime "created_at", null: false t.string "name" t.string "signing_secret", null: false diff --git a/db/migrate/20251112093037_create_search_indices.rb b/db/migrate/20251112093037_create_search_indices.rb index 0e8d5b612..cbad229b3 100644 --- a/db/migrate/20251112093037_create_search_indices.rb +++ b/db/migrate/20251112093037_create_search_indices.rb @@ -1,11 +1,11 @@ -class CreateSearchIndex < ActiveRecord::Migration[8.2] +class CreateSearchIndices < ActiveRecord::Migration[8.2] def up 16.times do |i| create_table "search_index_#{i}".to_sym do |t| t.string :searchable_type, null: false - t.bigint :searchable_id, null: false - t.bigint :card_id, null: false - t.bigint :board_id, null: false + t.uuid :searchable_id, null: false + t.uuid :card_id, null: false + t.uuid :board_id, null: false t.string :title t.text :content t.datetime :created_at, null: false diff --git a/db/schema.rb b/db/schema.rb index 5cc74b296..106a7325c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,21 +11,21 @@ # It's strongly recommended that you check this file into your version control system. ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do - create_table "accesses", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "accesses", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.datetime "accessed_at" - t.string "board_id", limit: 25, null: false + t.binary "board_id", null: false, limit: 16 t.datetime "created_at", null: false t.string "involvement", default: "access_only", null: false t.datetime "updated_at", null: false - t.string "user_id", limit: 25, null: false + t.binary "user_id", null: false, limit: 16 t.index ["accessed_at"], name: "index_accesses_on_accessed_at", order: :desc t.index ["board_id", "user_id"], name: "index_accesses_on_board_id_and_user_id", unique: true t.index ["board_id"], name: "index_accesses_on_board_id" t.index ["user_id"], name: "index_accesses_on_user_id" end - create_table "account_join_codes", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "account_id", limit: 25 + create_table "account_join_codes", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.binary "account_id", limit: 16 t.string "code", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false @@ -34,7 +34,7 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["code"], name: "index_account_join_codes_on_code", unique: true end - create_table "accounts", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "accounts", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.bigint "cards_count", default: 0, null: false t.datetime "created_at", null: false t.integer "external_account_id" @@ -43,27 +43,27 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["external_account_id"], name: "index_accounts_on_external_account_id", unique: true end - create_table "action_text_rich_texts", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.text "body", size: :long + create_table "action_text_rich_texts", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.text "body" t.datetime "created_at", null: false t.string "name", null: false - t.string "record_id", limit: 25, null: false + t.binary "record_id", null: false, limit: 16 t.string "record_type", null: false t.datetime "updated_at", null: false t.index ["record_type", "record_id", "name"], name: "index_action_text_rich_texts_uniqueness", unique: true end - create_table "active_storage_attachments", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "blob_id", limit: 25, null: false + create_table "active_storage_attachments", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.binary "blob_id", null: false, limit: 16 t.datetime "created_at", null: false t.string "name", null: false - t.string "record_id", limit: 25, null: false + t.binary "record_id", null: false, limit: 16 t.string "record_type", null: false t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id" t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true end - create_table "active_storage_blobs", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "active_storage_blobs", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.bigint "byte_size", null: false t.string "checksum" t.string "content_type" @@ -75,38 +75,38 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true end - create_table "active_storage_variant_records", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "blob_id", limit: 25, null: false + create_table "active_storage_variant_records", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.binary "blob_id", null: false, limit: 16 t.string "variation_digest", null: false t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true end create_table "assignees_filters", id: false, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "assignee_id", limit: 25, null: false - t.string "filter_id", limit: 25, null: false + t.binary "assignee_id", null: false, limit: 16 + t.binary "filter_id", null: false, limit: 16 t.index ["assignee_id"], name: "index_assignees_filters_on_assignee_id" t.index ["filter_id"], name: "index_assignees_filters_on_filter_id" end create_table "assigners_filters", id: false, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "assigner_id", limit: 25, null: false - t.string "filter_id", limit: 25, null: false + t.binary "assigner_id", null: false, limit: 16 + t.binary "filter_id", null: false, limit: 16 t.index ["assigner_id"], name: "index_assigners_filters_on_assigner_id" t.index ["filter_id"], name: "index_assigners_filters_on_filter_id" end - create_table "assignments", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "assignee_id", limit: 25, null: false - t.string "assigner_id", limit: 25, null: false - t.string "card_id", limit: 25, null: false + create_table "assignments", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.binary "assignee_id", null: false, limit: 16 + t.binary "assigner_id", null: false, limit: 16 + t.binary "card_id", null: false, limit: 16 t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["assignee_id", "card_id"], name: "index_assignments_on_assignee_id_and_card_id", unique: true t.index ["card_id"], name: "index_assignments_on_card_id" end - create_table "board_publications", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "board_id", limit: 25, null: false + create_table "board_publications", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.binary "board_id", null: false, limit: 16 t.datetime "created_at", null: false t.string "key" t.datetime "updated_at", null: false @@ -114,32 +114,32 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["key"], name: "index_board_publications_on_key", unique: true end - create_table "boards", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "account_id", limit: 25 + create_table "boards", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.binary "account_id", limit: 16 t.boolean "all_access", default: false, null: false t.datetime "created_at", null: false - t.string "creator_id", limit: 25, null: false + t.binary "creator_id", null: false, limit: 16 t.string "name", null: false t.datetime "updated_at", null: false t.index ["creator_id"], name: "index_boards_on_creator_id" end create_table "boards_filters", id: false, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "board_id", limit: 25, null: false - t.string "filter_id", limit: 25, null: false + t.binary "board_id", null: false, limit: 16 + t.binary "filter_id", null: false, limit: 16 t.index ["board_id"], name: "index_boards_filters_on_board_id" t.index ["filter_id"], name: "index_boards_filters_on_filter_id" end - create_table "card_activity_spikes", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "card_id", limit: 25, null: false + create_table "card_activity_spikes", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.binary "card_id", null: false, limit: 16 t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["card_id"], name: "index_card_activity_spikes_on_card_id" end - create_table "card_engagements", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "card_id", limit: 25 + create_table "card_engagements", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.binary "card_id", limit: 16 t.datetime "created_at", null: false t.string "status", default: "doing", null: false t.datetime "updated_at", null: false @@ -147,28 +147,28 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["status"], name: "index_card_engagements_on_status" end - create_table "card_goldnesses", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "card_id", limit: 25, null: false + create_table "card_goldnesses", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.binary "card_id", null: false, limit: 16 t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["card_id"], name: "index_card_goldnesses_on_card_id", unique: true end - create_table "card_not_nows", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "card_id", limit: 25, null: false + create_table "card_not_nows", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.binary "card_id", null: false, limit: 16 t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.string "user_id", limit: 25 + t.binary "user_id", limit: 16 t.index ["card_id"], name: "index_card_not_nows_on_card_id", unique: true t.index ["user_id"], name: "index_card_not_nows_on_user_id" end - create_table "cards", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "account_id", limit: 25 - t.string "board_id", limit: 25, null: false - t.string "column_id", limit: 25 + create_table "cards", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.binary "account_id", limit: 16 + t.binary "board_id", null: false, limit: 16 + t.binary "column_id", limit: 16 t.datetime "created_at", null: false - t.string "creator_id", limit: 25, null: false + t.binary "creator_id", null: false, limit: 16 t.date "due_on" t.datetime "last_active_at", null: false t.bigint "number", null: false @@ -182,25 +182,25 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do end create_table "closers_filters", id: false, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "closer_id", limit: 25, null: false - t.string "filter_id", limit: 25, null: false + t.binary "closer_id", null: false, limit: 16 + t.binary "filter_id", null: false, limit: 16 t.index ["closer_id"], name: "index_closers_filters_on_closer_id" t.index ["filter_id"], name: "index_closers_filters_on_filter_id" end - create_table "closures", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "card_id", limit: 25, null: false + create_table "closures", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.binary "card_id", null: false, limit: 16 t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.string "user_id", limit: 25 + t.binary "user_id", limit: 16 t.index ["card_id", "created_at"], name: "index_closures_on_card_id_and_created_at" t.index ["card_id"], name: "index_closures_on_card_id", unique: true t.index ["user_id"], name: "index_closures_on_user_id" end - create_table "columns", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "account_id", limit: 25 - t.string "board_id", limit: 25, null: false + create_table "columns", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.binary "account_id", limit: 16 + t.binary "board_id", null: false, limit: 16 t.string "color", null: false t.datetime "created_at", null: false t.string "name", null: false @@ -210,25 +210,25 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["board_id"], name: "index_columns_on_board_id" end - create_table "comments", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "account_id", limit: 25 - t.string "card_id", limit: 25, null: false + create_table "comments", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.binary "account_id", limit: 16 + t.binary "card_id", null: false, limit: 16 t.datetime "created_at", null: false - t.string "creator_id", limit: 25, null: false + t.binary "creator_id", null: false, limit: 16 t.datetime "updated_at", null: false t.index ["card_id"], name: "index_comments_on_card_id" end create_table "creators_filters", id: false, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "creator_id", limit: 25, null: false - t.string "filter_id", limit: 25, null: false + t.binary "creator_id", null: false, limit: 16 + t.binary "filter_id", null: false, limit: 16 t.index ["creator_id"], name: "index_creators_filters_on_creator_id" t.index ["filter_id"], name: "index_creators_filters_on_filter_id" end - create_table "entropies", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "entropies", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.bigint "auto_postpone_period", default: 2592000, null: false - t.string "container_id", limit: 25, null: false + t.binary "container_id", null: false, limit: 16 t.string "container_type", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false @@ -236,13 +236,13 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["container_type", "container_id"], name: "index_entropy_configurations_on_container", unique: true end - create_table "events", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "account_id", limit: 25 + create_table "events", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.binary "account_id", limit: 16 t.string "action", null: false - t.string "board_id", limit: 25, null: false + t.binary "board_id", null: false, limit: 16 t.datetime "created_at", null: false - t.string "creator_id", limit: 25, null: false - t.string "eventable_id", limit: 25, null: false + t.binary "creator_id", null: false, limit: 16 + t.binary "eventable_id", null: false, limit: 16 t.string "eventable_type", null: false t.json "particulars", default: -> { "(json_object())" } t.datetime "updated_at", null: false @@ -253,10 +253,10 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["eventable_type", "eventable_id"], name: "index_events_on_eventable" end - create_table "filters", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "account_id", limit: 25 + create_table "filters", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.binary "account_id", limit: 16 t.datetime "created_at", null: false - t.string "creator_id", limit: 25, null: false + t.binary "creator_id", null: false, limit: 16 t.json "fields", default: -> { "(json_object())" }, null: false t.string "params_digest", null: false t.datetime "updated_at", null: false @@ -264,33 +264,34 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do end create_table "filters_tags", id: false, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "filter_id", limit: 25, null: false - t.string "tag_id", limit: 25, null: false + t.binary "filter_id", null: false, limit: 16 + t.binary "tag_id", null: false, limit: 16 t.index ["filter_id"], name: "index_filters_tags_on_filter_id" t.index ["tag_id"], name: "index_filters_tags_on_tag_id" end - create_table "identities", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "identities", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", 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", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "magic_links", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "code", null: false t.datetime "created_at", null: false t.datetime "expires_at", null: false - t.bigint "identity_id" + t.binary "identity_id", limit: 16 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", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "memberships", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.datetime "created_at", null: false - t.bigint "identity_id", null: false + t.binary "identity_id", null: false, limit: 16 + 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" @@ -298,11 +299,11 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["tenant"], name: "index_memberships_on_user_tenant_and_user_id" end - create_table "mentions", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "mentions", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.datetime "created_at", null: false - t.string "mentionee_id", limit: 25, null: false - t.string "mentioner_id", limit: 25, null: false - t.string "source_id", limit: 25, null: false + t.binary "mentionee_id", null: false, limit: 16 + t.binary "mentioner_id", null: false, limit: 16 + t.binary "source_id", null: false, limit: 16 t.string "source_type", null: false t.datetime "updated_at", null: false t.index ["mentionee_id"], name: "index_mentions_on_mentionee_id" @@ -310,53 +311,53 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["source_type", "source_id"], name: "index_mentions_on_source" end - create_table "notification_bundles", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "account_id", limit: 25 + create_table "notification_bundles", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.binary "account_id", limit: 16 t.datetime "created_at", null: false t.datetime "ends_at", null: false t.datetime "starts_at", null: false t.integer "status", default: 0, null: false t.datetime "updated_at", null: false - t.string "user_id", limit: 25, null: false + t.binary "user_id", null: false, limit: 16 t.index ["ends_at", "status"], name: "index_notification_bundles_on_ends_at_and_status" t.index ["user_id", "starts_at", "ends_at"], name: "idx_on_user_id_starts_at_ends_at_7eae5d3ac5" t.index ["user_id", "status"], name: "index_notification_bundles_on_user_id_and_status" end - create_table "notifications", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "account_id", limit: 25 + create_table "notifications", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.binary "account_id", limit: 16 t.datetime "created_at", null: false - t.string "creator_id", limit: 25 + t.binary "creator_id", limit: 16 t.datetime "read_at" - t.string "source_id", limit: 25, null: false + t.binary "source_id", null: false, limit: 16 t.string "source_type", null: false t.datetime "updated_at", null: false - t.string "user_id", limit: 25, null: false + t.binary "user_id", null: false, limit: 16 t.index ["creator_id"], name: "index_notifications_on_creator_id" t.index ["source_type", "source_id"], name: "index_notifications_on_source" t.index ["user_id", "read_at", "created_at"], name: "index_notifications_on_user_id_and_read_at_and_created_at", order: { read_at: :desc, created_at: :desc } t.index ["user_id"], name: "index_notifications_on_user_id" end - create_table "pins", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "card_id", limit: 25, null: false + create_table "pins", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.binary "card_id", null: false, limit: 16 t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.string "user_id", limit: 25, null: false + t.binary "user_id", null: false, limit: 16 t.index ["card_id", "user_id"], name: "index_pins_on_card_id_and_user_id", unique: true t.index ["card_id"], name: "index_pins_on_card_id" t.index ["user_id"], name: "index_pins_on_user_id" end - create_table "push_subscriptions", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "account_id", limit: 25 + create_table "push_subscriptions", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.binary "account_id", limit: 16 t.string "auth_key" t.datetime "created_at", null: false t.string "endpoint" t.string "p256dh_key" t.datetime "updated_at", null: false t.string "user_agent" - t.string "user_id", limit: 25, null: false + t.binary "user_id", null: false, limit: 16 t.index ["endpoint", "p256dh_key", "auth_key"], name: "idx_on_endpoint_p256dh_key_auth_key_7553014576" t.index ["endpoint"], name: "index_push_subscriptions_on_endpoint" t.index ["user_agent"], name: "index_push_subscriptions_on_user_agent" @@ -364,22 +365,22 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["user_id"], name: "index_push_subscriptions_on_user_id" end - create_table "reactions", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "comment_id", limit: 25, null: false + create_table "reactions", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.binary "comment_id", null: false, limit: 16 t.string "content", limit: 16, null: false t.datetime "created_at", null: false - t.string "reacter_id", limit: 25, null: false + t.binary "reacter_id", null: false, limit: 16 t.datetime "updated_at", null: false t.index ["comment_id"], name: "index_reactions_on_comment_id" t.index ["reacter_id"], name: "index_reactions_on_reacter_id" end create_table "search_index_0", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "board_id", limit: 25, null: false - t.string "card_id", limit: 25, null: false + t.binary "board_id", null: false, limit: 16 + t.binary "card_id", null: false, limit: 16 t.text "content" t.datetime "created_at", null: false - t.string "searchable_id", limit: 25, null: false + t.binary "searchable_id", null: false, limit: 16 t.string "searchable_type", null: false t.string "title" t.index ["content", "title"], name: "idx_si0_fulltext", type: :fulltext @@ -387,11 +388,11 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do end create_table "search_index_1", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "board_id", limit: 25, null: false - t.string "card_id", limit: 25, null: false + t.binary "board_id", null: false, limit: 16 + t.binary "card_id", null: false, limit: 16 t.text "content" t.datetime "created_at", null: false - t.string "searchable_id", limit: 25, null: false + t.binary "searchable_id", null: false, limit: 16 t.string "searchable_type", null: false t.string "title" t.index ["content", "title"], name: "idx_si1_fulltext", type: :fulltext @@ -399,11 +400,11 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do end create_table "search_index_10", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "board_id", limit: 25, null: false - t.string "card_id", limit: 25, null: false + t.binary "board_id", null: false, limit: 16 + t.binary "card_id", null: false, limit: 16 t.text "content" t.datetime "created_at", null: false - t.string "searchable_id", limit: 25, null: false + t.binary "searchable_id", null: false, limit: 16 t.string "searchable_type", null: false t.string "title" t.index ["content", "title"], name: "idx_si10_fulltext", type: :fulltext @@ -411,11 +412,11 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do end create_table "search_index_11", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "board_id", limit: 25, null: false - t.string "card_id", limit: 25, null: false + t.binary "board_id", null: false, limit: 16 + t.binary "card_id", null: false, limit: 16 t.text "content" t.datetime "created_at", null: false - t.string "searchable_id", limit: 25, null: false + t.binary "searchable_id", null: false, limit: 16 t.string "searchable_type", null: false t.string "title" t.index ["content", "title"], name: "idx_si11_fulltext", type: :fulltext @@ -423,11 +424,11 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do end create_table "search_index_12", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "board_id", limit: 25, null: false - t.string "card_id", limit: 25, null: false + t.binary "board_id", null: false, limit: 16 + t.binary "card_id", null: false, limit: 16 t.text "content" t.datetime "created_at", null: false - t.string "searchable_id", limit: 25, null: false + t.binary "searchable_id", null: false, limit: 16 t.string "searchable_type", null: false t.string "title" t.index ["content", "title"], name: "idx_si12_fulltext", type: :fulltext @@ -435,11 +436,11 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do end create_table "search_index_13", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "board_id", limit: 25, null: false - t.string "card_id", limit: 25, null: false + t.binary "board_id", null: false, limit: 16 + t.binary "card_id", null: false, limit: 16 t.text "content" t.datetime "created_at", null: false - t.string "searchable_id", limit: 25, null: false + t.binary "searchable_id", null: false, limit: 16 t.string "searchable_type", null: false t.string "title" t.index ["content", "title"], name: "idx_si13_fulltext", type: :fulltext @@ -447,11 +448,11 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do end create_table "search_index_14", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "board_id", limit: 25, null: false - t.string "card_id", limit: 25, null: false + t.binary "board_id", null: false, limit: 16 + t.binary "card_id", null: false, limit: 16 t.text "content" t.datetime "created_at", null: false - t.string "searchable_id", limit: 25, null: false + t.binary "searchable_id", null: false, limit: 16 t.string "searchable_type", null: false t.string "title" t.index ["content", "title"], name: "idx_si14_fulltext", type: :fulltext @@ -459,11 +460,11 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do end create_table "search_index_15", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "board_id", limit: 25, null: false - t.string "card_id", limit: 25, null: false + t.binary "board_id", null: false, limit: 16 + t.binary "card_id", null: false, limit: 16 t.text "content" t.datetime "created_at", null: false - t.string "searchable_id", limit: 25, null: false + t.binary "searchable_id", null: false, limit: 16 t.string "searchable_type", null: false t.string "title" t.index ["content", "title"], name: "idx_si15_fulltext", type: :fulltext @@ -471,11 +472,11 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do end create_table "search_index_2", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "board_id", limit: 25, null: false - t.string "card_id", limit: 25, null: false + t.binary "board_id", null: false, limit: 16 + t.binary "card_id", null: false, limit: 16 t.text "content" t.datetime "created_at", null: false - t.string "searchable_id", limit: 25, null: false + t.binary "searchable_id", null: false, limit: 16 t.string "searchable_type", null: false t.string "title" t.index ["content", "title"], name: "idx_si2_fulltext", type: :fulltext @@ -483,11 +484,11 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do end create_table "search_index_3", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "board_id", limit: 25, null: false - t.string "card_id", limit: 25, null: false + t.binary "board_id", null: false, limit: 16 + t.binary "card_id", null: false, limit: 16 t.text "content" t.datetime "created_at", null: false - t.string "searchable_id", limit: 25, null: false + t.binary "searchable_id", null: false, limit: 16 t.string "searchable_type", null: false t.string "title" t.index ["content", "title"], name: "idx_si3_fulltext", type: :fulltext @@ -495,11 +496,11 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do end create_table "search_index_4", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "board_id", limit: 25, null: false - t.string "card_id", limit: 25, null: false + t.binary "board_id", null: false, limit: 16 + t.binary "card_id", null: false, limit: 16 t.text "content" t.datetime "created_at", null: false - t.string "searchable_id", limit: 25, null: false + t.binary "searchable_id", null: false, limit: 16 t.string "searchable_type", null: false t.string "title" t.index ["content", "title"], name: "idx_si4_fulltext", type: :fulltext @@ -507,11 +508,11 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do end create_table "search_index_5", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "board_id", limit: 25, null: false - t.string "card_id", limit: 25, null: false + t.binary "board_id", null: false, limit: 16 + t.binary "card_id", null: false, limit: 16 t.text "content" t.datetime "created_at", null: false - t.string "searchable_id", limit: 25, null: false + t.binary "searchable_id", null: false, limit: 16 t.string "searchable_type", null: false t.string "title" t.index ["content", "title"], name: "idx_si5_fulltext", type: :fulltext @@ -519,11 +520,11 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do end create_table "search_index_6", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "board_id", limit: 25, null: false - t.string "card_id", limit: 25, null: false + t.binary "board_id", null: false, limit: 16 + t.binary "card_id", null: false, limit: 16 t.text "content" t.datetime "created_at", null: false - t.string "searchable_id", limit: 25, null: false + t.binary "searchable_id", null: false, limit: 16 t.string "searchable_type", null: false t.string "title" t.index ["content", "title"], name: "idx_si6_fulltext", type: :fulltext @@ -531,11 +532,11 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do end create_table "search_index_7", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "board_id", limit: 25, null: false - t.string "card_id", limit: 25, null: false + t.binary "board_id", null: false, limit: 16 + t.binary "card_id", null: false, limit: 16 t.text "content" t.datetime "created_at", null: false - t.string "searchable_id", limit: 25, null: false + t.binary "searchable_id", null: false, limit: 16 t.string "searchable_type", null: false t.string "title" t.index ["content", "title"], name: "idx_si7_fulltext", type: :fulltext @@ -543,11 +544,11 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do end create_table "search_index_8", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "board_id", limit: 25, null: false - t.string "card_id", limit: 25, null: false + t.binary "board_id", null: false, limit: 16 + t.binary "card_id", null: false, limit: 16 t.text "content" t.datetime "created_at", null: false - t.string "searchable_id", limit: 25, null: false + t.binary "searchable_id", null: false, limit: 16 t.string "searchable_type", null: false t.string "title" t.index ["content", "title"], name: "idx_si8_fulltext", type: :fulltext @@ -555,44 +556,44 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do end create_table "search_index_9", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "board_id", limit: 25, null: false - t.string "card_id", limit: 25, null: false + t.binary "board_id", null: false, limit: 16 + t.binary "card_id", null: false, limit: 16 t.text "content" t.datetime "created_at", null: false - t.string "searchable_id", limit: 25, null: false + t.binary "searchable_id", null: false, limit: 16 t.string "searchable_type", null: false t.string "title" t.index ["content", "title"], name: "idx_si9_fulltext", type: :fulltext t.index ["searchable_type", "searchable_id"], name: "idx_si9_type_id", unique: true end - create_table "search_queries", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "search_queries", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.datetime "created_at", null: false t.string "terms", limit: 2000, null: false t.datetime "updated_at", null: false - t.string "user_id", limit: 25, null: false + t.binary "user_id", null: false, limit: 16 t.index ["user_id", "terms"], name: "index_search_queries_on_user_id_and_terms", length: { terms: 255 } t.index ["user_id", "updated_at"], name: "index_search_queries_on_user_id_and_updated_at", unique: true t.index ["user_id"], name: "index_search_queries_on_user_id" end - create_table "search_results", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "search_results", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.datetime "created_at", null: false t.datetime "updated_at", null: false end - create_table "sessions", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "sessions", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.datetime "created_at", null: false - t.bigint "identity_id", null: false + t.binary "identity_id", null: false, limit: 16 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 - create_table "steps", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "account_id", limit: 25 - t.string "card_id", limit: 25, null: false + create_table "steps", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.binary "account_id", limit: 16 + t.binary "card_id", null: false, limit: 16 t.boolean "completed", default: false, null: false t.text "content", null: false t.datetime "created_at", null: false @@ -601,38 +602,38 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["card_id"], name: "index_steps_on_card_id" end - create_table "taggings", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "card_id", limit: 25, null: false + create_table "taggings", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.binary "card_id", null: false, limit: 16 t.datetime "created_at", null: false - t.string "tag_id", limit: 25, null: false + t.binary "tag_id", null: false, limit: 16 t.datetime "updated_at", null: false t.index ["card_id", "tag_id"], name: "index_taggings_on_card_id_and_tag_id", unique: true t.index ["tag_id"], name: "index_taggings_on_tag_id" end - create_table "tags", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "account_id", limit: 25 + create_table "tags", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.binary "account_id", limit: 16 t.datetime "created_at", null: false t.string "title" t.datetime "updated_at", null: false t.index ["title"], name: "index_tags_on_title", unique: true end - create_table "user_settings", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "user_settings", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "bundle_email_frequency", default: 0, null: false t.datetime "created_at", null: false t.string "timezone_name" t.datetime "updated_at", null: false - t.string "user_id", limit: 25, null: false + t.binary "user_id", null: false, limit: 16 t.index ["user_id", "bundle_email_frequency"], name: "index_user_settings_on_user_id_and_bundle_email_frequency" t.index ["user_id"], name: "index_user_settings_on_user_id" end - create_table "users", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "account_id", limit: 25 + create_table "users", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.binary "account_id", limit: 16 t.boolean "active", default: true, null: false t.datetime "created_at", null: false - t.integer "membership_id" + t.binary "membership_id", limit: 16 t.string "name", null: false t.string "role", default: "member", null: false t.datetime "updated_at", null: false @@ -640,42 +641,42 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["role"], name: "index_users_on_role" end - create_table "watches", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "card_id", limit: 25, null: false + create_table "watches", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.binary "card_id", null: false, limit: 16 t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.string "user_id", limit: 25, null: false + t.binary "user_id", null: false, limit: 16 t.boolean "watching", default: true, null: false t.index ["card_id"], name: "index_watches_on_card_id" t.index ["user_id", "card_id"], name: "index_watches_on_user_id_and_card_id" t.index ["user_id"], name: "index_watches_on_user_id" end - create_table "webhook_delinquency_trackers", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "webhook_delinquency_trackers", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "consecutive_failures_count", default: 0 t.datetime "created_at", null: false t.datetime "first_failure_at" t.datetime "updated_at", null: false - t.string "webhook_id", limit: 25, null: false + t.binary "webhook_id", null: false, limit: 16 t.index ["webhook_id"], name: "index_webhook_delinquency_trackers_on_webhook_id" end - create_table "webhook_deliveries", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "webhook_deliveries", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.datetime "created_at", null: false - t.string "event_id", limit: 25, null: false + t.binary "event_id", null: false, limit: 16 t.text "request" t.text "response" t.string "state", null: false t.datetime "updated_at", null: false - t.string "webhook_id", limit: 25, null: false + t.binary "webhook_id", null: false, limit: 16 t.index ["event_id"], name: "index_webhook_deliveries_on_event_id" t.index ["webhook_id"], name: "index_webhook_deliveries_on_webhook_id" end - create_table "webhooks", id: { type: :string, limit: 25 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "account_id", limit: 25 + create_table "webhooks", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.binary "account_id", limit: 16 t.boolean "active", default: true, null: false - t.string "board_id", limit: 25, null: false + t.binary "board_id", null: false, limit: 16 t.datetime "created_at", null: false t.string "name" t.string "signing_secret", null: false diff --git a/lib/rails_ext/active_record_uuid_type.rb b/lib/rails_ext/active_record_uuid_type.rb new file mode 100644 index 000000000..46ff73017 --- /dev/null +++ b/lib/rails_ext/active_record_uuid_type.rb @@ -0,0 +1,37 @@ +# Custom UUID attribute type for MySQL binary storage with base36 string representation +module ActiveRecord + module Type + class Uuid < Binary + BASE36_LENGTH = 25 # 36^25 > 2^128 + + def self.generate + uuid = SecureRandom.uuid_v7 + hex = uuid.delete("-") + hex.to_i(16).to_s(36).rjust(25, "0") + end + + def serialize(value) + return unless value + + hex = value.to_s.to_i(36).to_s(16).rjust(32, "0") + binary = hex.scan(/../).map(&:hex).pack("C*") + binary.force_encoding(Encoding::BINARY) + end + + def deserialize(value) + return unless value + + hex = value.unpack1("H*") + hex.to_i(16).to_s(36).rjust(BASE36_LENGTH, "0") + end + + def cast(value) + deserialize(serialize(value)) + end + + end + end +end + +# Register the UUID type for Trilogy adapter +ActiveRecord::Type.register(:uuid, ActiveRecord::Type::Uuid, adapter: :trilogy) diff --git a/script/import-sqlite-database.rb b/script/import-sqlite-database.rb index f1030eaa3..9d4459a3c 100755 --- a/script/import-sqlite-database.rb +++ b/script/import-sqlite-database.rb @@ -101,7 +101,7 @@ class Import end def generate_uuid - UuidPrimaryKey.generate + ActiveRecord::Type::Uuid.generate end def setup_account diff --git a/test/models/comment/searchable_test.rb b/test/models/comment/searchable_test.rb index af7860483..36528f1e7 100644 --- a/test/models/comment/searchable_test.rb +++ b/test/models/comment/searchable_test.rb @@ -21,18 +21,25 @@ class Comment::SearchableTest < ActiveSupport::TestCase test "comment search" do table_name = Searchable.search_index_table_name(@account.id) + uuid_type = ActiveRecord::Type.lookup(:uuid, adapter: :trilogy) # Comment is indexed on create comment = @card.comments.create!(body: "searchable comment text", creator: @user) result = ActiveRecord::Base.connection.execute( - "SELECT COUNT(*) FROM #{table_name} WHERE searchable_type = 'Comment' AND searchable_id = '#{comment.id}'" + ActiveRecord::Base.sanitize_sql([ + "SELECT COUNT(*) FROM #{table_name} WHERE searchable_type = 'Comment' AND searchable_id = ?", + uuid_type.serialize(comment.id) + ]) ).first[0] assert_equal 1, result # Comment is updated in index comment.update!(body: "updated text") content = ActiveRecord::Base.connection.execute( - "SELECT content FROM #{table_name} WHERE searchable_type = 'Comment' AND searchable_id = '#{comment.id}'" + ActiveRecord::Base.sanitize_sql([ + "SELECT content FROM #{table_name} WHERE searchable_type = 'Comment' AND searchable_id = ?", + uuid_type.serialize(comment.id) + ]) ).first[0] assert_match /updat/, content @@ -40,7 +47,10 @@ class Comment::SearchableTest < ActiveSupport::TestCase comment_id = comment.id comment.destroy result = ActiveRecord::Base.connection.execute( - "SELECT COUNT(*) FROM #{table_name} WHERE searchable_type = 'Comment' AND searchable_id = '#{comment_id}'" + ActiveRecord::Base.sanitize_sql([ + "SELECT COUNT(*) FROM #{table_name} WHERE searchable_type = 'Comment' AND searchable_id = ?", + uuid_type.serialize(comment_id) + ]) ).first[0] assert_equal 0, result @@ -55,9 +65,13 @@ class Comment::SearchableTest < ActiveSupport::TestCase # Comment stores parent card_id and board_id new_comment = @card.comments.create!(body: "test comment", creator: @user) row = ActiveRecord::Base.connection.execute( - "SELECT card_id, board_id FROM #{table_name} WHERE searchable_type = 'Comment' AND searchable_id = '#{new_comment.id}'" + ActiveRecord::Base.sanitize_sql([ + "SELECT card_id, board_id FROM #{table_name} WHERE searchable_type = 'Comment' AND searchable_id = ?", + uuid_type.serialize(new_comment.id) + ]) ).first - assert_equal @card.id, row[0] - assert_equal @board.id, row[1] + # Deserialize binary UUIDs from result + assert_equal @card.id, uuid_type.deserialize(row[0]) + assert_equal @board.id, uuid_type.deserialize(row[1]) end end diff --git a/test/test_helper.rb b/test/test_helper.rb index 14eddd534..4c46eb758 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -136,9 +136,10 @@ module FixturesTestHelper bytes[14] = (rand_b >> 8) & 0xff bytes[15] = rand_b & 0xff - # Format as UUID string + # Format as UUID string and convert to base36 (25 chars) uuid = "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" % bytes - UuidPrimaryKey.uuid_to_base36(uuid) + hex = uuid.delete("-") + hex.to_i(16).to_s(36).rjust(25, "0") end end end