diff --git a/Gemfile b/Gemfile index 103b7688..1066cbfa 100644 --- a/Gemfile +++ b/Gemfile @@ -30,6 +30,7 @@ gem 'sidekiq-cron' gem 'sidekiq-limit_fetch' gem 'sprockets-rails' gem 'stimulus-rails' +gem 'strong_migrations' gem 'tailwindcss-rails' gem 'turbo-rails' gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby] @@ -54,6 +55,7 @@ group :test do end group :development do + gem 'database_consistency', require: false gem 'foreman' gem 'rubocop-rails', require: false end diff --git a/Gemfile.lock b/Gemfile.lock index 47ea71bd..73829ee8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -109,6 +109,8 @@ GEM data_migrate (11.2.0) activerecord (>= 6.1) railties (>= 6.1) + database_consistency (2.0.0) + activerecord (>= 3.2) date (3.4.1) debug (1.10.0) irb (~> 1.10) @@ -389,6 +391,8 @@ GEM stimulus-rails (1.3.4) railties (>= 6.0.0) stringio (3.1.2) + strong_migrations (2.1.0) + activerecord (>= 6.1) super_diff (0.14.0) attr_extras (>= 6.2.4) diff-lcs @@ -437,6 +441,7 @@ DEPENDENCIES bootsnap chartkick data_migrate + database_consistency debug devise dotenv-rails @@ -473,6 +478,7 @@ DEPENDENCIES simplecov sprockets-rails stimulus-rails + strong_migrations super_diff tailwindcss-rails turbo-rails diff --git a/app/models/user.rb b/app/models/user.rb index 64e45425..0dd32104 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -18,6 +18,11 @@ class User < ApplicationRecord after_create :create_api_key before_save :strip_trailing_slashes + validates :email, presence: true + validates :reset_password_token, uniqueness: true, allow_nil: true + + attribute :admin, :boolean, default: false + def countries_visited stats.pluck(:toponyms).flatten.map { _1['country'] }.uniq.compact end diff --git a/config/initializers/strong_migrations.rb b/config/initializers/strong_migrations.rb new file mode 100644 index 00000000..04e43a9e --- /dev/null +++ b/config/initializers/strong_migrations.rb @@ -0,0 +1,26 @@ +# Mark existing migrations as safe +StrongMigrations.start_after = 20241225175637 + +# Set timeouts for migrations +# If you use PgBouncer in transaction mode, delete these lines and set timeouts on the database user +StrongMigrations.lock_timeout = 10.seconds +StrongMigrations.statement_timeout = 1.hour + +# Analyze tables after indexes are added +# Outdated statistics can sometimes hurt performance +StrongMigrations.auto_analyze = true + +# Set the version of the production database +# so the right checks are run in development +# StrongMigrations.target_version = 10 + +# Add custom checks +# StrongMigrations.add_check do |method, args| +# if method == :add_index && args[0].to_s == "users" +# stop! "No more indexes on the users table" +# end +# end + +# Make some operations safe by default +# See https://github.com/ankane/strong_migrations#safe-by-default +# StrongMigrations.safe_by_default = true diff --git a/db/migrate/20241226202204_add_database_users_constraints.rb b/db/migrate/20241226202204_add_database_users_constraints.rb new file mode 100644 index 00000000..04247aeb --- /dev/null +++ b/db/migrate/20241226202204_add_database_users_constraints.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class AddDatabaseUsersConstraints < ActiveRecord::Migration[8.0] + def change + add_check_constraint :users, 'email IS NOT NULL', name: 'users_email_null', validate: false + add_check_constraint :users, 'admin IS NOT NULL', name: 'users_admin_null', validate: false + end +end diff --git a/db/migrate/20241226202831_validate_add_database_users_constraints.rb b/db/migrate/20241226202831_validate_add_database_users_constraints.rb new file mode 100644 index 00000000..d05c606b --- /dev/null +++ b/db/migrate/20241226202831_validate_add_database_users_constraints.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class ValidateAddDatabaseUsersConstraints < ActiveRecord::Migration[8.0] + def up + validate_check_constraint :users, name: 'users_email_null' + change_column_null :users, :email, false + remove_check_constraint :users, name: 'users_email_null' + end + + def down + add_check_constraint :users, 'email IS NOT NULL', name: 'users_email_null', validate: false + change_column_null :users, :email, true + end +end diff --git a/db/schema.rb b/db/schema.rb index a79c53a9..0b55b25e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2024_12_11_113119) do +ActiveRecord::Schema[8.0].define(version: 2024_12_26_202831) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -219,6 +219,8 @@ ActiveRecord::Schema[7.2].define(version: 2024_12_11_113119) do t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true end + add_check_constraint "users", "admin IS NOT NULL", name: "users_admin_null", validate: false + create_table "visits", force: :cascade do |t| t.bigint "area_id" t.bigint "user_id", null: false