add new privacy option to auto-defederate after a given timespan + add options to defederate and/or delete past posts + add `defed_in`/`parent:defed_in`/`thread:defed_in` bangtags + ui indicator for posts marked for auto-defederation

master
multiple creatures 2020-01-13 21:57:24 -06:00
parent 1fbe7c3402
commit 67516a07db
24 changed files with 391 additions and 21 deletions

View File

@ -53,6 +53,7 @@ class Api::V1::StatusesController < Api::BaseController
visibility: status_params[:visibility],
scheduled_at: status_params[:scheduled_at],
delete_after: status_params[:delete_after],
defederate_after: status_params[:defederate_after],
sharekey: status_params[:sharekey],
application: doorkeeper_token.application,
poll: status_params[:poll],
@ -100,6 +101,7 @@ class Api::V1::StatusesController < Api::BaseController
:sharekey,
:scheduled_at,
:delete_after,
:defederate_after,
:content_type,
media_ids: [],
poll: [

View File

@ -12,6 +12,12 @@ class Settings::PreferencesController < Settings::BaseController
def update
user_settings.update(user_settings_params.to_h)
MarkExpiredStatusesWorker.perform_async(
current_account.id,
truthy_param?(:setting_defederate_old),
truthy_param?(:setting_lifespan_old)
)
if current_user.update(user_params)
I18n.locale = current_user.locale
toggle_filters
@ -85,6 +91,7 @@ class Settings::PreferencesController < Settings::BaseController
:setting_max_public_history,
:setting_max_public_access,
:setting_roar_lifespan,
:setting_roar_defederate,
:setting_delayed_roars,
:setting_delayed_for,
:setting_boost_interval,

View File

@ -64,6 +64,9 @@ export default class StatusIcons extends React.PureComponent {
{status.get('delete_after') ? (
<i className='fa fa-clock-o' title={new Date(status.get('delete_after'))} aria-hidden='true' />
) : null}
{status.get('defederate_after') ? (
<i className='fa fa-calendar-times-o' title={new Date(status.get('defederate_after'))} aria-hidden='true' />
) : null}
{status.get('reject_replies') ? (
<i className='fa fa-microphone-slash' title='Rejecting replies' aria-hidden='true' />
) : null}

View File

@ -131,6 +131,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
let favouriteLink = '';
let sharekeyLinks = '';
let destructIcon = '';
let defederateIcon = '';
let rejectIcon = '';
if (this.props.measureHeight) {
@ -256,6 +257,14 @@ export default class DetailedStatus extends ImmutablePureComponent {
)
}
if (status.get('defederate_after')) {
defederateIcon = (
<span>
<i className='fa fa-calendar-times-o' title={new Date(status.get('defederate_after'))} /> ·
</span>
)
}
if (status.get('reject_replies')) {
rejectIcon = (
<span>
@ -285,7 +294,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
/>
<div className='detailed-status__meta'>
{sharekeyLinks} {reblogLink} · {favouriteLink} · {destructIcon} {rejectIcon} <VisibilityIcon visibility={status.get('visibility')} />
{sharekeyLinks} {reblogLink} · {favouriteLink} · {defederateIcon} {destructIcon} {rejectIcon} <VisibilityIcon visibility={status.get('visibility')} />
<a className='detailed-status__datetime' href={status.get('url')} target='_blank' rel='noopener'>
<FormattedDate value={new Date(status.get('created_at'))} hour12={false} year='numeric' month='short' day='2-digit' hour='2-digit' minute='2-digit' />
</a>

View File

@ -59,6 +59,21 @@ class Bangtags
['all', 'live'] => ['live', 'all'],
['all', 'lifespan'] => ['lifespan', 'all'],
['all', 'delete_in'] => ['delete_in', 'all'],
['parent', 'd'] => ['defederate_in', 'parent'],
['parent', 'defed'] => ['defederate_in', 'parent'],
['parent', 'defed_in'] => ['defederate_in', 'parent'],
['parent', 'defederate'] => ['defederate_in', 'parent'],
['thread', 'd'] => ['defederate_in', 'thread'],
['thread', 'defed'] => ['defederate_in', 'thread'],
['thread', 'defed_in'] => ['defederate_in', 'thread'],
['thread', 'defederate'] => ['defederate_in', 'thread'],
['all', 'd'] => ['defederate_in', 'all'],
['all', 'defed'] => ['defederate_in', 'all'],
['all', 'defed_in'] => ['defederate_in', 'all'],
['all', 'defederate'] => ['defederate_in', 'all'],
}
# sections of the final status text
@ -730,6 +745,50 @@ class Bangtags
s.delete_after = delete_after
Rails.cache.delete("statuses/#{s.id}")
end
when 'd', 'defed', 'defed_in', 'defederate', 'defederate_in'
chunk = nil
next if cmd[1].nil?
case cmd[1].downcase
when 'parent', 'thread', 'all'
s = cmd[1].downcase.to_sym
s = @parent_status if s == :parent
next unless s == :all || @parent_status.present?
next unless s == :thread || s == :all || @parent_status.account_id == @account.id
i = cmd[2].to_i
unit = cmd[3].present? ? cmd[3].downcase : 'minutes'
else
s = @status
i = cmd[1].to_i
unit = cmd[2].present? ? cmd[2].downcase : 'minutes'
end
defederate_after = case unit
when 'min', 'mins', 'minute', 'minutes'
i.minutes
when 'h', 'hr', 'hrs', 'hour', 'hours'
i.hours
when 'd', 'dy', 'dys', 'day', 'days'
i.days
when 'w', 'wk', 'wks', 'week', 'weeks'
i.weeks
when 'm', 'mn', 'mns', 'month', 'months'
i.months
when 'y', 'yr', 'yrs', 'year', 'years'
i.years
end
if s == :thread
@parent_status.conversation.statuses.where(account_id: @account.id).find_each do |s|
s.defederate_after = defederate_after
Rails.cache.delete("statuses/#{s.id}")
end
elsif s == :all
@account.statuses.find_each do |s|
s.defederate_after = defederate_after
Rails.cache.delete("statuses/#{s.id}")
end
else
s.defederate_after = defederate_after
Rails.cache.delete("statuses/#{s.id}")
end
when 'keysmash'
keyboard = [
'asdf', 'jkl;',

View File

@ -42,6 +42,9 @@ class UserSettingsDecorator
user.settings['max_public_history'] = max_public_history_preference if change?('setting_max_public_history')
user.settings['max_public_access'] = max_public_access_preference if change?('setting_max_public_access')
user.settings['roar_lifespan'] = roar_lifespan_preference if change?('setting_roar_lifespan')
user.settings['roar_lifespan_old'] = roar_lifespan_old_preference if change?('setting_roar_lifespan_old')
user.settings['roar_defederate'] = roar_defederate_preference if change?('setting_roar_defederate')
user.settings['roar_defederate_old'] = roar_defederate_old_preference if change?('setting_roar_defederate_old')
user.settings['delayed_roars'] = delayed_roars_preference if change?('setting_delayed_roars')
user.settings['delayed_for'] = delayed_for_preference if change?('setting_delayed_for')
user.settings['boost_interval'] = boost_interval_preference if change?('setting_boost_interval')
@ -160,6 +163,18 @@ class UserSettingsDecorator
settings['setting_roar_lifespan']
end
def roar_lifespan_old_preference
settings['setting_roar_lifespan_old']
end
def roar_defederate_preference
settings['setting_roar_defederate']
end
def roar_defederate_old_preference
settings['setting_roar_defederate_old']
end
def delayed_for_preference
settings['setting_delayed_for']
end

View File

@ -135,6 +135,9 @@ class Account < ApplicationRecord
:max_public_history,
:max_public_access,
:roar_lifespan,
:roar_lifespan_old,
:roar_defederate,
:roar_defederate_old,
:delayed_roars?,
:hides_public_profile?,

View File

@ -0,0 +1,21 @@
# == Schema Information
#
# Table name: defederating_statuses
#
# id :bigint(8) not null, primary key
# status_id :bigint(8)
# defederate_after :datetime
#
class DefederatingStatus < ApplicationRecord
belongs_to :status, inverse_of: :defederating_status
validate :validate_future_date
validates :status_id, uniqueness: true
private
def validate_future_date
errors.add(:defederate_after, I18n.t('defederating_statuses.too_soon')) if defederate_after.present? && defederate_after < Time.now.utc + PostStatusService::MIN_DESTRUCT_OFFSET
end
end

View File

@ -11,6 +11,7 @@ class DestructingStatus < ApplicationRecord
belongs_to :status, inverse_of: :destructing_status
validate :validate_future_date
validates :status_id, uniqueness: true
private

View File

@ -82,6 +82,7 @@ class Status < ApplicationRecord
has_one :status_stat, inverse_of: :status
has_one :poll, inverse_of: :status, dependent: :destroy
has_one :destructing_status, inverse_of: :status, dependent: :destroy
has_one :defederating_status, inverse_of: :status, dependent: :destroy
has_one :imported_status, inverse_of: :status, dependent: :destroy
has_one :sharekey, inverse_of: :status, dependent: :destroy
@ -284,6 +285,10 @@ class Status < ApplicationRecord
end
def delete_after=(value)
if defederate_after && defederate_after < (Time.now.utc + 5.minutes + value)
value = 5.minutes + value
end
if destructing_status.nil?
DestructingStatus.create!(status_id: id, delete_after: Time.now.utc + value)
else
@ -291,6 +296,20 @@ class Status < ApplicationRecord
end
end
def defederate_after
defederating_status&.defederate_after
end
def defederate_after=(value)
return unless delete_after.nil? || delete_after >= (Time.now.utc + 5.minutes + value)
if defederating_status.nil?
DefederatingStatus.create!(status_id: id, defederate_after: Time.now.utc + value)
else
defederating_status.defederate_after = Time.now.utc + value
end
end
def mark_for_mass_destruction!
@marked_for_mass_destruction = true
end

View File

@ -148,6 +148,9 @@ class User < ApplicationRecord
:max_public_history,
:max_public_access,
:roar_lifespan,
:roar_lifespan_old,
:roar_defederate,
:roar_defederate_old,
:delayed_roars,
:delayed_for,
:boost_interval,
@ -340,6 +343,18 @@ class User < ApplicationRecord
@_roar_lifespan ||= [0, (settings.roar_lifespan || 0).to_i].max
end
def roar_lifespan_old
@_roar_lifespan_old ||= (settings.roar_lifespan_old || false)
end
def roar_defederate
@_roar_defederate ||= [0, (settings.roar_defederate || 0).to_i].max
end
def roar_defederate_old
@_roar_defederate_old ||= (settings.roar_defederate_old || false)
end
def delayed_roars?
@delayed_roars ||= (settings.delayed_roars || false)
end

View File

@ -16,6 +16,7 @@ class REST::StatusSerializer < ActiveModel::Serializer
attribute :local_only if :local?
attribute :sharekey, if: :has_sharekey?
attribute :delete_after, if: :current_user?
attribute :defederate_after, if: :current_user?
attribute :content, unless: :source_requested?
attribute :text, if: :source_requested?
@ -154,6 +155,10 @@ class REST::StatusSerializer < ActiveModel::Serializer
object.delete_after
end
def defederate_after
object.defederate_after
end
def reject_replies
object.reject_replies == true
end

View File

@ -30,6 +30,7 @@ class PostStatusService < BaseService
# @option [String] :language
# @option [String] :scheduled_at
# @option [String] :delete_after
# @option [String] :defederate_after
# @option [Account] :mentions Optional accounts to mention out-of-body
# @option [Boolean] :noreplies Author does not accept replies
# @option [Boolean] :nocrawl Optional skip link card generation
@ -76,6 +77,7 @@ class PostStatusService < BaseService
distribute: @options[:distribute],
nocrawl: @options[:nocrawl],
delete_after: @delete_after.nil? ? nil : @delete_after + 1.minute,
defederate_after: @defederate_after.nil? ? nil : @defederate_after + 1.minute,
reject_replies: @options[:noreplies] || false,
}.compact
@ -201,6 +203,18 @@ class PostStatusService < BaseService
end
@delete_after = nil if @delete_after.present? && (@delete_after < MIN_DESTRUCT_OFFSET)
case @options[:defederate_after].class
when NilClass
@defederate_after = @account.user.setting_roar_defederate.to_i.days
when ActiveSupport::Duration
@defederate_after = @options[:defederate_after]
when Integer
@defederate_after = @options[:defederate_after].minutes
when Float
@defederate_after = @options[:defederate_after].minutes
end
@defederate_after = nil if @defederate_after.present? && (@defederate_after < MIN_DESTRUCT_OFFSET)
rescue ArgumentError
raise ActiveRecord::RecordInvalid
end
@ -310,6 +324,7 @@ class PostStatusService < BaseService
visibility: @visibility,
local_only: @local_only,
delete_after: @delete_after,
defederate_after: @defederate_after,
reject_replies: @options[:noreplies] || false,
sharekey: @options[:sharekey],
language: language_from_option(@options[:language]) || @account.user_default_language&.presence || 'en',

View File

@ -16,22 +16,24 @@ class RemoveStatusService < BaseService
@stream_entry = status.stream_entry
@options = options
RedisLock.acquire(lock_options) do |lock|
if lock.acquired?
remove_from_queued
remove_from_self if status.account.local?
remove_from_followers
remove_from_lists
remove_from_affected
remove_reblogs
remove_from_hashtags
remove_from_public
remove_from_media if status.media_attachments.any?
remove_from_direct if status.direct_visibility?
unless options[:defederate_only]
RedisLock.acquire(lock_options) do |lock|
if lock.acquired?
remove_from_queued
remove_from_self if status.account.local?
remove_from_followers
remove_from_lists
remove_from_affected
remove_reblogs
remove_from_hashtags
remove_from_public
remove_from_media if status.media_attachments.any?
remove_from_direct if status.direct_visibility?
@status.destroy!
else
raise Mastodon::RaceConditionError
@status.destroy!
else
raise Mastodon::RaceConditionError
end
end
end
@ -44,6 +46,9 @@ class RemoveStatusService < BaseService
remove_from_remote_followers
remove_from_remote_affected
@status.update(local_only: true) if options[:defederate_only]
Rails.cache.delete("statuses/#{@status.id}")
end
private

View File

@ -17,9 +17,21 @@
= f.input :setting_default_privacy, collection: Status.selectable_visibilities, wrapper: :with_floating_label, include_blank: false, label_method: lambda { |visibility| safe_join([I18n.t("statuses.visibilities.#{visibility}"), content_tag(:span, I18n.t("statuses.visibilities.#{visibility}_long"), class: 'hint')]) }, required: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li'
.fields-group
= f.input :setting_max_public_history, collection: [1, 3, 6, 7, 14, 30, 60, 90, 180, 365, 730, 1095, 2190], wrapper: :with_label, include_blank: false, label_method: lambda { |item| safe_join([t("simple_form.labels.lifespan.#{item}")]) }, selected: current_user.max_public_history
= f.input :setting_max_public_access, collection: [1, 3, 6, 7, 14, 30, 60, 90, 180, 365, 730, 1095, 2190], wrapper: :with_label, include_blank: false, label_method: lambda { |item| safe_join([t("simple_form.labels.lifespan.#{item}")]) }, selected: current_user.max_public_access
= f.input :setting_roar_lifespan, collection: [0, 1, 3, 6, 7, 14, 30, 60, 90, 180, 365, 730, 1095, 2190], wrapper: :with_label, include_blank: false, label_method: lambda { |item| safe_join([t("simple_form.labels.lifespan.#{item}")]) }, selected: current_user.roar_lifespan
.fields-row
.fields-group.fields-row__column.fields-row__column-6
= f.input :setting_max_public_history, collection: [1, 3, 6, 7, 14, 30, 60, 90, 180, 365, 730, 1095, 2190], wrapper: :with_label, include_blank: false, label_method: lambda { |item| safe_join([t("simple_form.labels.lifespan.#{item}")]) }, selected: current_user.max_public_history
.fields-group.fields-row__column.fields-row__column-6
= f.input :setting_max_public_access, collection: [1, 3, 6, 7, 14, 30, 60, 90, 180, 365, 730, 1095, 2190], wrapper: :with_label, include_blank: false, label_method: lambda { |item| safe_join([t("simple_form.labels.lifespan.#{item}")]) }, selected: current_user.max_public_access
.fields-group
.fields-row
.fields-group.fields-row__column.fields-row__column-6
= f.input :setting_roar_defederate, collection: [0, 1, 3, 6, 7, 14, 30, 60, 90, 180, 365, 730, 1095, 2190], wrapper: :with_label, include_blank: false, label_method: lambda { |item| safe_join([t("simple_form.labels.lifespan.#{item}")]) }, selected: current_user.roar_defederate
= f.input :setting_roar_defederate_old, as: :boolean, wrapper: :with_label
.fields-group.fields-row__column.fields-row__column-6
= f.input :setting_roar_lifespan, collection: [0, 1, 3, 6, 7, 14, 30, 60, 90, 180, 365, 730, 1095, 2190], wrapper: :with_label, include_blank: false, label_method: lambda { |item| safe_join([t("simple_form.labels.lifespan.#{item}")]) }, selected: current_user.roar_lifespan
= f.input :setting_roar_lifespan_old, as: :boolean, wrapper: :with_label
.fields-group
= f.input :setting_default_sensitive, as: :boolean, wrapper: :with_label

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
class DefederateStatusWorker
include Sidekiq::Worker
sidekiq_options unique: :until_executed
def perform(defederating_status_id)
defederating_status = DefederatingStatus.find(defederating_status_id)
defederating_status.destroy!
RemoveStatusService.new.call(defederating_status.status, defederate_only: true)
true
rescue ActiveRecord::RecordNotFound, ActiveRecord::RecordInvalid
true
end
end

View File

@ -0,0 +1,44 @@
# frozen_string_literal: true
class MarkExpiredStatusesWorker
include Sidekiq::Worker
sidekiq_options queue: 'bulk'
def perform(account_id, defederate = false, lifespan = false)
@account = Account.find(account_id)
return if @account&.user.nil?
@user = @account.user
@roar_defederate = @user.roar_defederate.to_i
@roar_lifespan = @user.roar_lifespan.to_i
defederate = false if @roar_defederate == 0
lifespan = false if @roar_lifespan == 0
return unless defederate || lifespan
offset = 30.minutes
@account.statuses.find_each do |status|
modified = false
if defederate && !status.local_only? && status.updated_at < @roar_defederate.days.ago
status.defederate_after = offset
modified = true
end
if lifespan && status.updated_at < @roar_lifespan.days.ago
status.delete_after = offset + 30.minutes
modified = true
end
if modified
Rails.cache.delete("statuses/#{status.id}")
offset += 1.second
end
end
rescue ActiveRecord::RecordNotFound
true
end
end

View File

@ -0,0 +1,19 @@
# frozen_string_literal: true
class Scheduler::DefederatingStatusesScheduler
include Sidekiq::Worker
sidekiq_options unique: :until_executed, retry: 0
def perform
due_statuses.find_each do |defederating_status|
DefederateStatusWorker.perform_async(defederating_status.id)
end
end
private
def due_statuses
DefederatingStatus.where('defederate_after <= ?', Time.now.utc)
end
end

View File

@ -885,6 +885,8 @@ en:
too_soon: The scheduled timeframe must be at least 5 minutes into the future
destructing_statuses:
too_soon: The destruction timeframe must be at least 5 minutes into the future
defederating_statuses:
too_soon: The destruction timeframe must be at least 5 minutes into the future
sessions:
activity: Last activity
browser: Browser

View File

@ -49,6 +49,8 @@ en:
media_only: Hides text-only posts
filters_enabled: Enables custom timeline filters which can be configured in the Filters tab.
monsterfork_api: How much of Monsterfork features should be exposed to clients. Set to Basic or Vanilla if your Mastodon app is having problems with Monsterfork.
setting_roar_lifespan_old: "<strong>This is a destructive operation!</strong><br>Back up your account before saving changes!<br>Will deselect after save"
setting_roar_defederate_old: "<strong>This is an irreversible operation!</strong><br>Will deselect after save"
featured_tag:
name: 'You might want to use one of these:'
imports:
@ -131,6 +133,9 @@ en:
setting_default_language: Posting language
setting_default_privacy: Post privacy
setting_roar_lifespan: Auto-delete new roars after
setting_roar_lifespan_old: Include past roars
setting_roar_defederate: Auto-defederate new roars after
setting_roar_defederate_old: Include past roars
setting_delayed_roars: Delayed publishing of roars for proofreading
setting_delayed_for: Delay for
setting_boost_interval: Automatically space out consecutive boosts

View File

@ -13,6 +13,9 @@
destructing_statuses_scheduler:
every: '1m'
class: Scheduler::DestructingStatusesScheduler
defederatinging_statuses_scheduler:
every: '1m'
class: Scheduler::DefederatingStatusesScheduler
boosts_scheduler:
every: '1m'
class: Scheduler::BoostsScheduler

View File

@ -0,0 +1,9 @@
class CreateDefederatingStatuses < ActiveRecord::Migration[5.2]
def change
create_table :defederating_statuses do |t|
t.references :status, foreign_key: true
t.datetime :defederate_after
end
safety_assured { add_index :defederating_statuses, :defederate_after }
end
end

View File

@ -0,0 +1,11 @@
class AddUniqueIndexesOnDefederatingAndDestructingStatuses < ActiveRecord::Migration[5.2]
disable_ddl_transaction!
def change
remove_index :destructing_statuses, :status_id
remove_index :defederating_statuses, :status_id
add_index :destructing_statuses, :status_id, unique: true, algorithm: :concurrently
add_index :defederating_statuses, :status_id, unique: true, algorithm: :concurrently
end
end

View File

@ -896,6 +896,36 @@ CREATE SEQUENCE public.custom_filters_id_seq
ALTER SEQUENCE public.custom_filters_id_seq OWNED BY public.custom_filters.id;
--
-- Name: defederating_statuses; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE public.defederating_statuses (
id bigint NOT NULL,
status_id bigint,
defederate_after timestamp without time zone
);
--
-- Name: defederating_statuses_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE public.defederating_statuses_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: defederating_statuses_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE public.defederating_statuses_id_seq OWNED BY public.defederating_statuses.id;
--
-- Name: destructing_statuses; Type: TABLE; Schema: public; Owner: -
--
@ -2623,6 +2653,13 @@ ALTER TABLE ONLY public.custom_emojis ALTER COLUMN id SET DEFAULT nextval('publi
ALTER TABLE ONLY public.custom_filters ALTER COLUMN id SET DEFAULT nextval('public.custom_filters_id_seq'::regclass);
--
-- Name: defederating_statuses id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.defederating_statuses ALTER COLUMN id SET DEFAULT nextval('public.defederating_statuses_id_seq'::regclass);
--
-- Name: destructing_statuses id; Type: DEFAULT; Schema: public; Owner: -
--
@ -3084,6 +3121,14 @@ ALTER TABLE ONLY public.custom_filters
ADD CONSTRAINT custom_filters_pkey PRIMARY KEY (id);
--
-- Name: defederating_statuses defederating_statuses_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.defederating_statuses
ADD CONSTRAINT defederating_statuses_pkey PRIMARY KEY (id);
--
-- Name: destructing_statuses destructing_statuses_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
@ -3696,6 +3741,20 @@ CREATE UNIQUE INDEX index_custom_emojis_on_shortcode_and_domain ON public.custom
CREATE INDEX index_custom_filters_on_account_id ON public.custom_filters USING btree (account_id);
--
-- Name: index_defederating_statuses_on_defederate_after; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX index_defederating_statuses_on_defederate_after ON public.defederating_statuses USING btree (defederate_after);
--
-- Name: index_defederating_statuses_on_status_id; Type: INDEX; Schema: public; Owner: -
--
CREATE UNIQUE INDEX index_defederating_statuses_on_status_id ON public.defederating_statuses USING btree (status_id);
--
-- Name: index_destructing_statuses_on_delete_after; Type: INDEX; Schema: public; Owner: -
--
@ -3707,7 +3766,7 @@ CREATE INDEX index_destructing_statuses_on_delete_after ON public.destructing_st
-- Name: index_destructing_statuses_on_status_id; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX index_destructing_statuses_on_status_id ON public.destructing_statuses USING btree (status_id);
CREATE UNIQUE INDEX index_destructing_statuses_on_status_id ON public.destructing_statuses USING btree (status_id);
--
@ -4979,6 +5038,14 @@ ALTER TABLE ONLY public.account_warnings
ADD CONSTRAINT fk_rails_a7ebbb1e37 FOREIGN KEY (target_account_id) REFERENCES public.accounts(id) ON DELETE CASCADE;
--
-- Name: defederating_statuses fk_rails_af4e2f2cab; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.defederating_statuses
ADD CONSTRAINT fk_rails_af4e2f2cab FOREIGN KEY (status_id) REFERENCES public.statuses(id);
--
-- Name: web_push_subscriptions fk_rails_b006f28dac; Type: FK CONSTRAINT; Schema: public; Owner: -
--
@ -5399,6 +5466,8 @@ INSERT INTO "schema_migrations" (version) VALUES
('20200110214031'),
('20200110221801'),
('20200110221920'),
('20200111042543');
('20200111042543'),
('20200114011918'),
('20200114030940');