diff --git a/app/models/deletion_schedule.rb b/app/models/deletion_schedule.rb new file mode 100644 index 000000000..212087aee --- /dev/null +++ b/app/models/deletion_schedule.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true +# == Schema Information +# +# Table name: deletion_schedules +# +# id :bigint(8) not null, primary key +# user_id :bigint(8) +# delay :integer default(604800), not null +# created_at :datetime not null +# updated_at :datetime not null +# + +class DeletionSchedule < ApplicationRecord + belongs_to :user + + validates :delay, presence: true, numericality: { only_integer: true, greater_than: 7.days.seconds } +end diff --git a/app/models/user.rb b/app/models/user.rb index 453ffa8b0..b249ef4a1 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -70,6 +70,7 @@ class User < ApplicationRecord has_many :applications, class_name: 'Doorkeeper::Application', as: :owner has_many :backups, inverse_of: :user + has_one :deletion_schedule, dependent: :destroy validates :locale, inclusion: I18n.available_locales.map(&:to_s), if: :locale? validates_with BlacklistedEmailValidator, if: :email_changed? diff --git a/app/workers/deletion_schedule_worker.rb b/app/workers/deletion_schedule_worker.rb new file mode 100644 index 000000000..7dafb260b --- /dev/null +++ b/app/workers/deletion_schedule_worker.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +class DeletionScheduleWorker + include Sidekiq::Worker + + sidekiq_options unique: :until_executed, retry: 0 + + def perform(account_id, delay) + RemovalWorker.push_bulk(status_ids(account_id, snowflake_at(Time.now.utc - delay.seconds))) + end + + private + + def snowflake_at(time) + Mastodon::Snowflake.id_at(time) + end + + def status_ids(account_id, cut_off_id) + Status.where(account_id: account_id) + .where(Status.arel_table[:id].lt(cut_off_id)) + .reorder(id: :asc) + .limit(100) + .pluck(:id) + end +end diff --git a/app/workers/scheduler/deletion_scheduler.rb b/app/workers/scheduler/deletion_scheduler.rb new file mode 100644 index 000000000..20d2cb23f --- /dev/null +++ b/app/workers/scheduler/deletion_scheduler.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class Scheduler::DeletionScheduler + include Sidekiq::Worker + + sidekiq_options unique: :until_executed, retry: 0 + + def perform + DeletionSchedule.includes(:user).find_in_batches do |schedules| + DeletionScheduleWorker.push_bulk(schedules) do |schedule| + [schedule.user.account_id, schedule.delay] + end + end + end +end diff --git a/config/sidekiq.yml b/config/sidekiq.yml index c44af5b6c..18b780722 100644 --- a/config/sidekiq.yml +++ b/config/sidekiq.yml @@ -36,3 +36,6 @@ pghero_scheduler: cron: '0 0 * * *' class: Scheduler::PgheroScheduler + deletion_scheduler: + cron: '<%= Random.rand(0..59) %> <%= Random.rand(0..23) %> * * *' + class: Scheduler::DeletionScheduler diff --git a/db/migrate/20181127224209_create_deletion_schedules.rb b/db/migrate/20181127224209_create_deletion_schedules.rb new file mode 100644 index 000000000..1a97426f1 --- /dev/null +++ b/db/migrate/20181127224209_create_deletion_schedules.rb @@ -0,0 +1,10 @@ +class CreateDeletionSchedules < ActiveRecord::Migration[5.2] + def change + create_table :deletion_schedules do |t| + t.belongs_to :user, foreign_key: { on_delete: :cascade } + t.integer :delay, null: false, default: 7.days.seconds + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index d67521442..319a9cd1c 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.define(version: 2018_11_27_130500) do +ActiveRecord::Schema.define(version: 2018_11_27_224209) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -187,6 +187,14 @@ ActiveRecord::Schema.define(version: 2018_11_27_130500) do t.index ["account_id"], name: "index_custom_filters_on_account_id" end + create_table "deletion_schedules", force: :cascade do |t| + t.bigint "user_id" + t.integer "delay", default: 604800, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["user_id"], name: "index_deletion_schedules_on_user_id" + end + create_table "domain_blocks", force: :cascade do |t| t.string "domain", default: "", null: false t.datetime "created_at", null: false @@ -645,6 +653,7 @@ ActiveRecord::Schema.define(version: 2018_11_27_130500) do add_foreign_key "conversation_mutes", "accounts", name: "fk_225b4212bb", on_delete: :cascade add_foreign_key "conversation_mutes", "conversations", on_delete: :cascade add_foreign_key "custom_filters", "accounts", on_delete: :cascade + add_foreign_key "deletion_schedules", "users", on_delete: :cascade add_foreign_key "favourites", "accounts", name: "fk_5eb6c2b873", on_delete: :cascade add_foreign_key "favourites", "statuses", name: "fk_b0e856845e", on_delete: :cascade add_foreign_key "follow_requests", "accounts", column: "target_account_id", name: "fk_9291ec025d", on_delete: :cascade diff --git a/spec/fabricators/deletion_schedule_fabricator.rb b/spec/fabricators/deletion_schedule_fabricator.rb new file mode 100644 index 000000000..2b6ab161c --- /dev/null +++ b/spec/fabricators/deletion_schedule_fabricator.rb @@ -0,0 +1,4 @@ +Fabricator(:deletion_schedule) do + user + delay 30.days.seconds +end diff --git a/spec/models/deletion_schedule_spec.rb b/spec/models/deletion_schedule_spec.rb new file mode 100644 index 000000000..52efa276a --- /dev/null +++ b/spec/models/deletion_schedule_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe DeletionSchedule, type: :model do + +end