diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 23c61f2ed..1f0d3440c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -67,6 +67,13 @@ For Docker-based development (recommended): 3. Visit to see the AWBW portal page 4. Log in as a sample user with the default [credentials](#credentials) +### Database encoding +Different schema encodings on Mac vs Linux (utf8mb4 vs utf8) was causing some merge challenges in schema.rb. +To ensure consistent encoding across environments, we have set MySQL database to use `utf8mb4` encoding by default by +overriding DATABASE_URL in `docker-compose.yml`. + +``` + ## Dev seeds - Running mise should have run `rake db:seed` diff --git a/config/database.yml b/config/database.yml index 8329cfdaa..7461316cf 100644 --- a/config/database.yml +++ b/config/database.yml @@ -1,19 +1,20 @@ -base: &base - encoding: utf8 - url: <%= ENV.fetch("DATABASE_URL", "trilogy://root@localhost") %> +default: &default + adapter: trilogy + encoding: utf8mb4 # <-- forces UTF-8 everywhere + collation: utf8mb4_unicode_ci # <-- portable across MySQL 5.7/8.0 and MariaDB + pool: <%= ENV.fetch("RAILS_MAX_THREADS", 5) %> + user: root + password: + host: 127.0.0.1 + variables: + # Make sure the session uses utf8mb4 too: + collation_connection: utf8mb4_unicode_ci + sql_mode: STRICT_ALL_TABLES development: - <<: *base - pool: 5 + <<: *default database: awbw_development -# Warning: The database defined as "test" will be erased and -# re-generated from your development database when you run "rake". -# Do not set this db to the same as development or production. test: - <<: *base - pool: 5 + <<: *default database: awbw_test - -production: - <<: *base diff --git a/db/migrate/20251127143038_fix_charset_and_collation.rb b/db/migrate/20251127143038_fix_charset_and_collation.rb new file mode 100644 index 000000000..6856b54d3 --- /dev/null +++ b/db/migrate/20251127143038_fix_charset_and_collation.rb @@ -0,0 +1,22 @@ +class FixCharsetAndCollation < ActiveRecord::Migration[8.1] + DB = ActiveRecord::Base.connection_db_config.database + TARGET = "utf8mb4" + COLL = "utf8mb4_unicode_ci" + + def up + execute "ALTER DATABASE `#{DB}` CHARACTER SET #{TARGET} COLLATE #{COLL}" + tables.each do |t| + execute "ALTER TABLE `#{t}` CONVERT TO CHARACTER SET #{TARGET} COLLATE #{COLL}" + end + end + + def down + # no-op (irreversible) + end + + private + + def tables + ActiveRecord::Base.connection.tables - ["schema_migrations", "ar_internal_metadata"] + end +end diff --git a/db/schema.rb b/db/schema.rb index c48dbf708..67b058e2c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,8 +10,8 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.1].define(version: 2025_11_25_131123) do - create_table "active_storage_attachments", charset: "utf8mb3", force: :cascade do |t| +ActiveRecord::Schema[8.1].define(version: 2025_11_27_143038) do + create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.bigint "blob_id", null: false t.datetime "created_at", precision: nil, null: false t.string "name", null: false @@ -21,7 +21,7 @@ t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true end - create_table "active_storage_blobs", charset: "utf8mb3", force: :cascade do |t| + create_table "active_storage_blobs", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.string "aws_key" t.bigint "byte_size", null: false t.string "checksum" @@ -29,18 +29,18 @@ t.datetime "created_at", precision: nil, null: false t.string "filename", null: false t.string "key", null: false - t.text "metadata" + t.text "metadata", size: :medium t.string "service_name", null: false t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true end - create_table "active_storage_variant_records", charset: "utf8mb3", force: :cascade do |t| + create_table "active_storage_variant_records", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.bigint "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 "addresses", charset: "utf8mb3", force: :cascade do |t| + create_table "addresses", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.string "address_type" t.bigint "addressable_id" t.string "addressable_type" @@ -62,7 +62,7 @@ t.index ["addressable_type", "addressable_id"], name: "index_addresses_on_addressable" end - create_table "admins", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "admins", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.datetime "created_at", precision: nil t.datetime "current_sign_in_at", precision: nil t.string "current_sign_in_ip" @@ -81,7 +81,7 @@ t.index ["reset_password_token"], name: "index_admins_on_reset_password_token", unique: true end - create_table "age_ranges", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "age_ranges", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.datetime "created_at", precision: nil, null: false t.string "name" t.datetime "updated_at", precision: nil, null: false @@ -89,14 +89,14 @@ t.index ["windows_type_id"], name: "index_age_ranges_on_windows_type_id" end - create_table "answer_options", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "answer_options", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.datetime "created_at", precision: nil, null: false t.string "name" t.integer "order" t.datetime "updated_at", precision: nil, null: false end - create_table "attachments", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "attachments", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.datetime "created_at", precision: nil, null: false t.string "file_content_type" t.string "file_file_name" @@ -107,8 +107,8 @@ t.datetime "updated_at", precision: nil, null: false end - create_table "banners", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.text "content" + create_table "banners", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| + t.text "content", size: :medium t.datetime "created_at", precision: nil, null: false t.integer "created_by_id" t.boolean "show" @@ -118,15 +118,15 @@ t.index ["updated_by_id"], name: "index_banners_on_updated_by_id" end - create_table "bookmark_annotations", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.text "annotation", size: :medium + create_table "bookmark_annotations", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| + t.text "annotation", size: :long t.integer "bookmark_id" t.datetime "created_at", precision: nil, null: false t.datetime "updated_at", precision: nil, null: false t.index ["bookmark_id"], name: "index_bookmark_annotations_on_bookmark_id" end - create_table "bookmarks", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "bookmarks", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.integer "bookmarkable_id" t.string "bookmarkable_type" t.datetime "created_at", precision: nil, null: false @@ -135,7 +135,7 @@ t.index ["user_id"], name: "index_bookmarks_on_user_id" end - create_table "categories", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "categories", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.datetime "created_at", precision: nil, null: false t.integer "legacy_id" t.integer "metadatum_id" @@ -145,7 +145,7 @@ t.index ["metadatum_id"], name: "index_categories_on_metadatum_id" end - create_table "categorizable_items", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "categorizable_items", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.integer "categorizable_id" t.string "categorizable_type" t.integer "category_id" @@ -157,7 +157,7 @@ t.index ["category_id"], name: "index_categorizable_items_on_category_id" end - create_table "ckeditor_assets", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "ckeditor_assets", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.string "actual_url" t.integer "assetable_id" t.string "assetable_type", limit: 30 @@ -173,9 +173,9 @@ t.index ["assetable_type", "type", "assetable_id"], name: "idx_ckeditor_assetable_type" end - create_table "community_news", charset: "utf8mb3", force: :cascade do |t| + create_table "community_news", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.integer "author_id", null: false - t.text "body" + t.text "body", size: :medium t.datetime "created_at", null: false t.integer "created_by_id", null: false t.boolean "featured" @@ -194,7 +194,7 @@ t.index ["windows_type_id"], name: "index_community_news_on_windows_type_id" end - create_table "event_registrations", charset: "utf8mb3", force: :cascade do |t| + create_table "event_registrations", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.datetime "created_at", null: false t.bigint "event_id" t.integer "registrant_id" @@ -204,10 +204,10 @@ t.index ["registrant_id"], name: "index_event_registrations_on_registrant_id" end - create_table "events", charset: "utf8mb3", force: :cascade do |t| + create_table "events", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.datetime "created_at", null: false t.integer "created_by_id" - t.text "description" + t.text "description", size: :medium t.datetime "end_date", precision: nil t.boolean "featured", default: false, null: false t.boolean "publicly_visible", default: false, null: false @@ -218,8 +218,8 @@ t.index ["created_by_id"], name: "index_events_on_created_by_id" end - create_table "facilitators", id: :integer, charset: "utf8mb3", force: :cascade do |t| - t.text "bio" + create_table "facilitators", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| + t.text "bio", size: :medium t.string "city" t.string "country" t.datetime "created_at", null: false @@ -264,8 +264,8 @@ t.index ["updated_by_id"], name: "index_facilitators_on_updated_by_id" end - create_table "faqs", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.text "answer", size: :medium + create_table "faqs", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| + t.text "answer", size: :long t.datetime "created_at", precision: nil, null: false t.boolean "inactive" t.integer "ordering" @@ -273,7 +273,7 @@ t.datetime "updated_at", precision: nil, null: false end - create_table "footers", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "footers", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.string "adult_program" t.string "children_program" t.datetime "created_at", precision: nil, null: false @@ -282,9 +282,9 @@ t.datetime "updated_at", precision: nil, null: false end - create_table "form_builders", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "form_builders", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.datetime "created_at", precision: nil, null: false - t.text "description", size: :medium + t.text "description", size: :long t.string "name" t.integer "owner_type" t.datetime "updated_at", precision: nil, null: false @@ -292,7 +292,7 @@ t.index ["windows_type_id"], name: "index_form_builders_on_windows_type_id" end - create_table "form_field_answer_options", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "form_field_answer_options", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.integer "answer_option_id" t.datetime "created_at", precision: nil, null: false t.integer "form_field_id" @@ -301,7 +301,7 @@ t.index ["form_field_id"], name: "index_form_field_answer_options_on_form_field_id" end - create_table "form_fields", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "form_fields", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.integer "answer_datatype" t.integer "answer_type" t.datetime "created_at", precision: nil, null: false @@ -316,7 +316,7 @@ t.index ["form_id"], name: "index_form_fields_on_form_id" end - create_table "forms", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "forms", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.datetime "created_at", precision: nil, null: false t.integer "form_builder_id" t.integer "owner_id" @@ -325,7 +325,7 @@ t.index ["form_builder_id"], name: "index_forms_on_form_builder_id" end - create_table "images", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "images", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.datetime "created_at", precision: nil, null: false t.string "file_content_type" t.string "file_file_name" @@ -340,7 +340,7 @@ t.index ["type"], name: "index_images_on_type" end - create_table "locations", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "locations", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.string "city" t.string "country" t.datetime "created_at", precision: nil, null: false @@ -348,7 +348,7 @@ t.datetime "updated_at", precision: nil, null: false end - create_table "media_files", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "media_files", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.string "file_content_type" t.string "file_file_name" t.integer "file_file_size" @@ -357,7 +357,7 @@ t.integer "workshop_log_id" end - create_table "metadata", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "metadata", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.datetime "created_at", precision: nil, null: false t.string "legacy_id" t.string "name" @@ -365,17 +365,17 @@ t.datetime "updated_at", precision: nil, null: false end - create_table "monthly_reports", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "monthly_reports", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.string "best_call_time" t.boolean "call_requested" - t.text "comments", size: :medium + t.text "comments", size: :long t.datetime "created_at", precision: nil, null: false - t.text "goals", size: :medium - t.text "goals_reached", size: :medium + t.text "goals", size: :long + t.text "goals_reached", size: :long t.boolean "mail_evaluations" t.string "month" - t.text "most_challenging", size: :medium - t.text "most_effective", size: :medium + t.text "most_challenging", size: :long + t.text "most_effective", size: :long t.string "name" t.string "num_new_participants" t.string "num_ongoing_participants" @@ -388,7 +388,7 @@ t.index ["project_user_id"], name: "index_monthly_reports_on_project_user_id" end - create_table "notifications", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "notifications", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.datetime "created_at", precision: nil, null: false t.integer "noticeable_id" t.string "noticeable_type" @@ -396,26 +396,26 @@ t.datetime "updated_at", precision: nil, null: false end - create_table "permissions", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "permissions", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.datetime "created_at", precision: nil, null: false t.integer "legacy_id" t.string "security_cat" t.datetime "updated_at", precision: nil, null: false end - create_table "project_obligations", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "project_obligations", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.datetime "created_at", precision: nil, null: false t.string "name" t.datetime "updated_at", precision: nil, null: false end - create_table "project_statuses", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "project_statuses", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.datetime "created_at", precision: nil, null: false t.string "name" t.datetime "updated_at", precision: nil, null: false end - create_table "project_users", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "project_users", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.integer "agency_id" t.datetime "created_at", precision: nil, null: false t.string "filemaker_code" @@ -430,11 +430,11 @@ t.index ["user_id"], name: "index_project_users_on_user_id" end - create_table "projects", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "projects", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.string "agency_type" t.string "agency_type_other" t.datetime "created_at", precision: nil, null: false - t.text "description", size: :medium + t.text "description", size: :long t.date "end_date" t.string "filemaker_code" t.boolean "inactive", default: false @@ -444,7 +444,7 @@ t.integer "location_id" t.string "mission_vision_values" t.string "name" - t.text "notes", size: :medium + t.text "notes", size: :long t.integer "project_status_id" t.date "start_date" t.datetime "updated_at", precision: nil, null: false @@ -455,7 +455,7 @@ t.index ["windows_type_id"], name: "index_projects_on_windows_type_id" end - create_table "quotable_item_quotes", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "quotable_item_quotes", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.datetime "created_at", precision: nil, null: false t.integer "legacy_id" t.integer "quotable_id" @@ -465,22 +465,22 @@ t.index ["quote_id"], name: "index_quotable_item_quotes_on_quote_id" end - create_table "quotes", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "quotes", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.string "age" t.datetime "created_at", precision: nil, null: false t.string "gender", limit: 1 t.boolean "inactive", default: true t.boolean "legacy", default: false t.integer "legacy_id" - t.text "quote", size: :medium + t.text "quote", size: :long t.string "speaker_name" t.datetime "updated_at", precision: nil, null: false t.integer "workshop_id" t.index ["workshop_id"], name: "index_quotes_on_workshop_id" end - create_table "report_form_field_answers", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.text "answer", size: :medium + create_table "report_form_field_answers", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| + t.text "answer", size: :long t.integer "answer_option_id" t.datetime "created_at", precision: nil t.integer "form_field_id" @@ -491,7 +491,7 @@ t.index ["report_id"], name: "index_report_form_field_answers_on_report_id" end - create_table "reports", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "reports", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.integer "adults_first_time", default: 0 t.integer "adults_ongoing", default: 0 t.integer "children_first_time", default: 0 @@ -521,7 +521,7 @@ t.index ["windows_type_id"], name: "index_reports_on_windows_type_id" end - create_table "resources", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "resources", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.string "agency" t.string "author" t.datetime "created_at", precision: nil, null: false @@ -534,7 +534,7 @@ t.integer "legacy_id" t.boolean "male", default: false t.integer "ordering" - t.text "text", size: :medium + t.text "text", size: :long t.string "title" t.datetime "updated_at", precision: nil, null: false t.string "url" @@ -546,7 +546,7 @@ t.index ["workshop_id"], name: "index_resources_on_workshop_id" end - create_table "sectorable_items", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "sectorable_items", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.datetime "created_at", precision: nil, null: false t.boolean "inactive", default: true t.boolean "is_leader", default: false, null: false @@ -558,15 +558,15 @@ t.index ["sectorable_type", "sectorable_id"], name: "index_sectorable_items_on_sectorable_type_and_sectorable_id" end - create_table "sectors", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "sectors", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.datetime "created_at", precision: nil, null: false t.string "name" t.boolean "published", default: false t.datetime "updated_at", precision: nil, null: false end - create_table "stories", charset: "utf8mb3", force: :cascade do |t| - t.text "body" + create_table "stories", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| + t.text "body", size: :medium t.datetime "created_at", null: false t.integer "created_by_id", null: false t.string "external_workshop_title" @@ -593,8 +593,8 @@ t.index ["workshop_id"], name: "index_stories_on_workshop_id" end - create_table "story_ideas", charset: "utf8mb3", force: :cascade do |t| - t.text "body" + create_table "story_ideas", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| + t.text "body", size: :medium t.datetime "created_at", null: false t.integer "created_by_id", null: false t.string "external_workshop_title" @@ -614,17 +614,17 @@ t.index ["workshop_id"], name: "index_story_ideas_on_workshop_id" end - create_table "user_form_form_fields", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "user_form_form_fields", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.datetime "created_at", precision: nil, null: false t.integer "form_field_id" - t.text "text", size: :medium + t.text "text", size: :long t.datetime "updated_at", precision: nil, null: false t.integer "user_form_id" t.index ["form_field_id"], name: "index_user_form_form_fields_on_form_field_id" t.index ["user_form_id"], name: "index_user_form_form_fields_on_user_form_id" end - create_table "user_forms", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "user_forms", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.datetime "created_at", precision: nil, null: false t.integer "form_id" t.datetime "updated_at", precision: nil, null: false @@ -633,7 +633,7 @@ t.index ["user_id"], name: "index_user_forms_on_user_id" end - create_table "user_permissions", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "user_permissions", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.datetime "created_at", precision: nil, null: false t.integer "permission_id" t.datetime "updated_at", precision: nil, null: false @@ -642,7 +642,7 @@ t.index ["user_id"], name: "index_user_permissions_on_user_id" end - create_table "users", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "users", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.string "address" t.string "address2" t.integer "agency_id" @@ -654,7 +654,7 @@ t.date "birthday" t.string "city" t.string "city2" - t.text "comment", size: :medium + t.text "comment", size: :long t.boolean "confirmed", default: true t.datetime "created_at", precision: nil t.datetime "current_sign_in_at", precision: nil @@ -669,7 +669,7 @@ t.string "last_sign_in_ip" t.boolean "legacy", default: false t.integer "legacy_id" - t.text "notes", size: :medium + t.text "notes", size: :long t.string "phone" t.string "phone2" t.string "phone3" @@ -691,7 +691,7 @@ t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true end - create_table "windows_types", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "windows_types", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.datetime "created_at", precision: nil, null: false t.integer "legacy_id" t.string "name" @@ -699,7 +699,7 @@ t.datetime "updated_at", precision: nil, null: false end - create_table "workshop_age_ranges", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "workshop_age_ranges", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.integer "age_range_id" t.datetime "created_at", precision: nil, null: false t.datetime "updated_at", precision: nil, null: false @@ -708,37 +708,37 @@ t.index ["workshop_id"], name: "index_workshop_age_ranges_on_workshop_id" end - create_table "workshop_ideas", charset: "utf8mb3", force: :cascade do |t| - t.text "age_range" - t.text "age_range_spanish" - t.text "closing" - t.text "closing_spanish" + create_table "workshop_ideas", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| + t.text "age_range", size: :medium + t.text "age_range_spanish", size: :medium + t.text "closing", size: :medium + t.text "closing_spanish", size: :medium t.datetime "created_at", null: false t.integer "created_by_id", null: false - t.text "creation" - t.text "creation_spanish" - t.text "demonstration" - t.text "demonstration_spanish" - t.text "description" - t.text "description_spanish" - t.text "instructions" - t.text "instructions_spanish" - t.text "introduction" - t.text "introduction_spanish" - t.text "materials" - t.text "materials_spanish" - t.text "misc_instructions_spanish" - t.text "notes" - t.text "notes_spanish" - t.text "objective" - t.text "objective_spanish" - t.text "opening_circle" - t.text "opening_circle_spanish" - t.text "optional_materials" - t.text "optional_materials_spanish" - t.text "setup" - t.text "setup_spanish" - t.text "staff_notes" + t.text "creation", size: :medium + t.text "creation_spanish", size: :medium + t.text "demonstration", size: :medium + t.text "demonstration_spanish", size: :medium + t.text "description", size: :medium + t.text "description_spanish", size: :medium + t.text "instructions", size: :medium + t.text "instructions_spanish", size: :medium + t.text "introduction", size: :medium + t.text "introduction_spanish", size: :medium + t.text "materials", size: :medium + t.text "materials_spanish", size: :medium + t.text "misc_instructions_spanish", size: :medium + t.text "notes", size: :medium + t.text "notes_spanish", size: :medium + t.text "objective", size: :medium + t.text "objective_spanish", size: :medium + t.text "opening_circle", size: :medium + t.text "opening_circle_spanish", size: :medium + t.text "optional_materials", size: :medium + t.text "optional_materials_spanish", size: :medium + t.text "setup", size: :medium + t.text "setup_spanish", size: :medium + t.text "staff_notes", size: :medium t.integer "time_closing" t.integer "time_creation" t.integer "time_demonstration" @@ -748,40 +748,40 @@ t.integer "time_opening" t.integer "time_opening_circle" t.integer "time_warm_up" - t.text "timeframe" - t.text "timeframe_spanish" - t.text "tips" - t.text "tips_spanish" + t.text "timeframe", size: :medium + t.text "timeframe_spanish", size: :medium + t.text "tips", size: :medium + t.text "tips_spanish", size: :medium t.string "title" t.datetime "updated_at", null: false t.integer "updated_by_id", null: false - t.text "visualization" - t.text "visualization_spanish" - t.text "warm_up" - t.text "warm_up_spanish" + t.text "visualization", size: :medium + t.text "visualization_spanish", size: :medium + t.text "warm_up", size: :medium + t.text "warm_up_spanish", size: :medium t.integer "windows_type_id", null: false t.index ["created_by_id"], name: "index_workshop_ideas_on_created_by_id" t.index ["updated_by_id"], name: "index_workshop_ideas_on_updated_by_id" t.index ["windows_type_id"], name: "index_workshop_ideas_on_windows_type_id" end - create_table "workshop_logs", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.text "challenges", size: :medium - t.text "comments", size: :medium + create_table "workshop_logs", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| + t.text "challenges", size: :long + t.text "comments", size: :long t.datetime "created_at", precision: nil, null: false t.date "date" - t.text "differences", size: :medium + t.text "differences", size: :long t.boolean "is_embodied_art_workshop", default: false t.boolean "lead_similar" t.integer "num_participants_first_time", default: 0 t.integer "num_participants_on_going", default: 0 t.integer "project_id" - t.text "questions", size: :medium + t.text "questions", size: :long t.integer "rating", default: 0 - t.text "reaction", size: :medium - t.text "similarities", size: :medium - t.text "successes", size: :medium - t.text "suggestions", size: :medium + t.text "reaction", size: :long + t.text "similarities", size: :long + t.text "successes", size: :long + t.text "suggestions", size: :long t.datetime "updated_at", precision: nil, null: false t.integer "user_id" t.integer "workshop_id" @@ -790,7 +790,7 @@ t.index ["workshop_id"], name: "index_workshop_logs_on_workshop_id" end - create_table "workshop_resources", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "workshop_resources", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.datetime "created_at", precision: nil, null: false t.integer "resource_id" t.datetime "updated_at", precision: nil, null: false @@ -799,7 +799,7 @@ t.index ["workshop_id"], name: "index_workshop_resources_on_workshop_id" end - create_table "workshop_series_memberships", charset: "utf8mb3", force: :cascade do |t| + create_table "workshop_series_memberships", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.datetime "created_at", null: false t.string "series_description" t.string "series_description_spanish" @@ -812,8 +812,8 @@ t.index ["workshop_parent_id", "workshop_child_id"], name: "index_workshop_series_memberships_on_parent_and_child", unique: true end - create_table "workshop_variations", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.text "code", size: :medium + create_table "workshop_variations", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| + t.text "code", size: :long t.datetime "created_at", precision: nil, null: false t.integer "created_by_id" t.boolean "inactive", default: true @@ -828,21 +828,21 @@ t.index ["workshop_id"], name: "index_workshop_variations_on_workshop_id" end - create_table "workshops", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.text "age_range", size: :medium - t.text "age_range_spanish", size: :medium + create_table "workshops", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| + t.text "age_range", size: :long + t.text "age_range_spanish", size: :long t.string "author_location" - t.text "closing", size: :medium - t.text "closing_spanish", size: :medium - t.datetime "created_at", precision: nil, null: false - t.text "creation", size: :medium - t.text "creation_spanish", size: :medium - t.text "demonstration", size: :medium - t.text "demonstration_spanish", size: :medium - t.text "description", size: :medium - t.text "description_spanish", size: :medium - t.text "extra_field" - t.text "extra_field_spanish" + t.text "closing", size: :long + t.text "closing_spanish", size: :long + t.datetime "created_at", precision: nil, null: false + t.text "creation", size: :long + t.text "creation_spanish", size: :long + t.text "demonstration", size: :long + t.text "demonstration_spanish", size: :long + t.text "description", size: :long + t.text "description_spanish", size: :long + t.text "extra_field", size: :medium + t.text "extra_field_spanish", size: :medium t.boolean "featured", default: false t.string "filemaker_code" t.string "full_name" @@ -851,37 +851,37 @@ t.integer "header_file_size" t.datetime "header_updated_at", precision: nil t.boolean "inactive", default: true - t.text "instructions", size: :medium - t.text "instructions_spanish", size: :medium - t.text "introduction", size: :medium - t.text "introduction_spanish", size: :medium + t.text "instructions", size: :long + t.text "instructions_spanish", size: :long + t.text "introduction", size: :long + t.text "introduction_spanish", size: :long t.integer "led_count", default: 0 t.boolean "legacy", default: false t.integer "legacy_id" - t.text "materials", size: :medium - t.text "materials_spanish", size: :medium + t.text "materials", size: :long + t.text "materials_spanish", size: :long t.string "misc1" - t.text "misc1_spanish", size: :medium + t.text "misc1_spanish", size: :long t.string "misc2" - t.text "misc2_spanish", size: :medium - t.text "misc_instructions", size: :medium - t.text "misc_instructions_spanish", size: :medium + t.text "misc2_spanish", size: :long + t.text "misc_instructions", size: :long + t.text "misc_instructions_spanish", size: :long t.integer "month" - t.text "notes", size: :medium - t.text "notes_spanish", size: :medium - t.text "objective", size: :medium - t.text "objective_spanish", size: :medium - t.text "opening_circle", size: :medium - t.text "opening_circle_spanish", size: :medium - t.text "optional_materials", size: :medium - t.text "optional_materials_spanish", size: :medium + t.text "notes", size: :long + t.text "notes_spanish", size: :long + t.text "objective", size: :long + t.text "objective_spanish", size: :long + t.text "opening_circle", size: :long + t.text "opening_circle_spanish", size: :long + t.text "optional_materials", size: :long + t.text "optional_materials_spanish", size: :long t.string "photo_caption" - t.text "project", size: :medium - t.text "project_spanish", size: :medium + t.text "project", size: :long + t.text "project_spanish", size: :long t.string "pub_issue" t.boolean "searchable", default: false - t.text "setup", size: :medium - t.text "setup_spanish", size: :medium + t.text "setup", size: :long + t.text "setup_spanish", size: :long t.string "thumbnail_content_type" t.string "thumbnail_file_name" t.integer "thumbnail_file_size" @@ -893,18 +893,18 @@ t.integer "time_opening" t.integer "time_opening_circle" t.integer "time_warm_up" - t.text "timeframe", size: :medium - t.text "timeframe_spanish", size: :medium - t.text "timestamps", size: :medium - t.text "tips", size: :medium - t.text "tips_spanish", size: :medium + t.text "timeframe", size: :long + t.text "timeframe_spanish", size: :long + t.text "timestamps", size: :long + t.text "tips", size: :long + t.text "tips_spanish", size: :long t.string "title" t.datetime "updated_at", precision: nil, null: false t.integer "user_id" - t.text "visualization", size: :medium - t.text "visualization_spanish", size: :medium - t.text "warm_up", size: :medium - t.text "warm_up_spanish", size: :medium + t.text "visualization", size: :long + t.text "visualization_spanish", size: :long + t.text "warm_up", size: :long + t.text "warm_up_spanish", size: :long t.integer "windows_type_id" t.bigint "workshop_idea_id" t.integer "year" @@ -920,7 +920,6 @@ t.index ["year", "month"], name: "index_workshops_on_year_and_month" end - add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" add_foreign_key "age_ranges", "windows_types" add_foreign_key "banners", "users", column: "created_by_id" diff --git a/docker-compose.yml b/docker-compose.yml index dae8aad09..507654aa1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,6 +7,7 @@ services: - 3306 volumes: - mysql-data:/var/lib/mysql + - ./docker/mysql/charset.cnf:/etc/mysql/conf.d/charset.cnf:ro - ./db/init:/docker-entrypoint-initdb.d environment: MYSQL_ALLOW_EMPTY_PASSWORD: 1 @@ -31,34 +32,20 @@ services: # If you prefer traditional keys, set DB_* in .env and configure database.yml accordingly. RAILS_ENV: development RAILS_LOG_TO_STDOUT: "true" - VITE_RUBY_HOST: vite - DATABASE_URL: trilogy://root@database:3306/ + DATABASE_URL: trilogy://root@database:3306/?encoding=utf8mb4&collation=utf8mb4_unicode_ci&variables[collation_connection]=utf8mb4_unicode_ci + ports: - 3000:3000 volumes: - ./:/app:cached - /app/tmp + working_dir: /app command: bash -lc "bin/rails s -b 0.0.0.0 -p 3000" tty: true stdin_open: true restart: unless-stopped - vite: - build: - context: . - dockerfile: Dockerfile.dev - env_file: .env - environment: - RAILS_ENV: development - RAILS_LOG_TO_STDOUT: "true" - VITE_RUBY_HOST: 0.0.0.0 - volumes: - - ./:/app:cached - - /app/tmp - command: bin/vite dev - ports: - - 3036:3036 prod: profiles: diff --git a/docker/mysql/charset.cnf b/docker/mysql/charset.cnf new file mode 100644 index 000000000..727b70091 --- /dev/null +++ b/docker/mysql/charset.cnf @@ -0,0 +1,3 @@ +[mysqld] +character-set-server = utf8mb4 +collation-server = utf8mb4_unicode_ci \ No newline at end of file