Merge commit '5fae2de454806730742b7be7435ae1c4fb97cf3c' into glitch-soc/merge-upstream
commit
aa57f7e3e2
4
Gemfile
4
Gemfile
|
@ -5,7 +5,7 @@ ruby '>= 3.0.0'
|
|||
|
||||
gem 'pkg-config', '~> 1.5'
|
||||
|
||||
gem 'puma', '~> 6.2'
|
||||
gem 'puma', '~> 6.3'
|
||||
gem 'rails', '~> 6.1.7'
|
||||
gem 'sprockets', '~> 3.7.2'
|
||||
gem 'thor', '~> 1.2'
|
||||
|
@ -17,7 +17,7 @@ gem 'makara', '~> 0.5'
|
|||
gem 'pghero'
|
||||
gem 'dotenv-rails', '~> 2.8'
|
||||
|
||||
gem 'aws-sdk-s3', '~> 1.122', require: false
|
||||
gem 'aws-sdk-s3', '~> 1.123', require: false
|
||||
gem 'fog-core', '<= 2.4.0'
|
||||
gem 'fog-openstack', '~> 0.3', require: false
|
||||
gem 'kt-paperclip', '~> 7.1', github: 'kreeti/kt-paperclip', ref: '11abf222dc31bff71160a1d138b445214f434b2b'
|
||||
|
|
27
Gemfile.lock
27
Gemfile.lock
|
@ -109,17 +109,17 @@ GEM
|
|||
attr_required (1.0.1)
|
||||
awrence (1.2.1)
|
||||
aws-eventstream (1.2.0)
|
||||
aws-partitions (1.761.0)
|
||||
aws-sdk-core (3.172.0)
|
||||
aws-partitions (1.772.0)
|
||||
aws-sdk-core (3.174.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.651.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
aws-sdk-kms (1.64.0)
|
||||
aws-sdk-core (~> 3, >= 3.165.0)
|
||||
aws-sdk-kms (1.65.0)
|
||||
aws-sdk-core (~> 3, >= 3.174.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.122.0)
|
||||
aws-sdk-core (~> 3, >= 3.165.0)
|
||||
aws-sdk-s3 (1.123.0)
|
||||
aws-sdk-core (~> 3, >= 3.174.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.4)
|
||||
aws-sigv4 (1.5.2)
|
||||
|
@ -501,7 +501,7 @@ GEM
|
|||
premailer (~> 1.7, >= 1.7.9)
|
||||
private_address_check (0.5.0)
|
||||
public_suffix (5.0.1)
|
||||
puma (6.2.2)
|
||||
puma (6.3.0)
|
||||
nio4r (~> 2.0)
|
||||
pundit (2.3.0)
|
||||
activesupport (>= 3.0.0)
|
||||
|
@ -544,8 +544,9 @@ GEM
|
|||
rails-dom-testing (2.0.3)
|
||||
activesupport (>= 4.2.0)
|
||||
nokogiri (>= 1.6)
|
||||
rails-html-sanitizer (1.5.0)
|
||||
loofah (~> 2.19, >= 2.19.1)
|
||||
rails-html-sanitizer (1.6.0)
|
||||
loofah (~> 2.21)
|
||||
nokogiri (~> 1.14)
|
||||
rails-i18n (6.0.0)
|
||||
i18n (>= 0.7, < 2)
|
||||
railties (>= 6.0.0, < 7)
|
||||
|
@ -588,7 +589,7 @@ GEM
|
|||
rspec-mocks (3.12.5)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.12.0)
|
||||
rspec-rails (6.0.2)
|
||||
rspec-rails (6.0.3)
|
||||
actionpack (>= 6.1)
|
||||
activesupport (>= 6.1)
|
||||
railties (>= 6.1)
|
||||
|
@ -648,7 +649,7 @@ GEM
|
|||
redis (>= 4.5.0, < 5)
|
||||
sidekiq-bulk (0.2.0)
|
||||
sidekiq
|
||||
sidekiq-scheduler (5.0.2)
|
||||
sidekiq-scheduler (5.0.3)
|
||||
rufus-scheduler (~> 3.2)
|
||||
sidekiq (>= 6, < 8)
|
||||
tilt (>= 1.4.0)
|
||||
|
@ -770,7 +771,7 @@ DEPENDENCIES
|
|||
active_model_serializers (~> 0.10)
|
||||
addressable (~> 2.8)
|
||||
annotate (~> 3.2)
|
||||
aws-sdk-s3 (~> 1.122)
|
||||
aws-sdk-s3 (~> 1.123)
|
||||
better_errors (~> 2.9)
|
||||
binding_of_caller (~> 1.0)
|
||||
blurhash (~> 0.1)
|
||||
|
@ -846,7 +847,7 @@ DEPENDENCIES
|
|||
premailer-rails
|
||||
private_address_check (~> 0.5)
|
||||
public_suffix (~> 5.0)
|
||||
puma (~> 6.2)
|
||||
puma (~> 6.3)
|
||||
pundit (~> 2.3)
|
||||
rack (~> 2.2.7)
|
||||
rack-attack (~> 6.6)
|
||||
|
|
|
@ -12,6 +12,7 @@ class Settings::ImportsController < Settings::BaseController
|
|||
muting: 'muted_accounts_failures.csv',
|
||||
domain_blocking: 'blocked_domains_failures.csv',
|
||||
bookmarks: 'bookmarks_failures.csv',
|
||||
lists: 'lists_failures.csv',
|
||||
}.freeze
|
||||
|
||||
TYPE_TO_HEADERS_MAP = {
|
||||
|
@ -20,6 +21,7 @@ class Settings::ImportsController < Settings::BaseController
|
|||
muting: ['Account address', 'Hide notifications'],
|
||||
domain_blocking: false,
|
||||
bookmarks: false,
|
||||
lists: false,
|
||||
}.freeze
|
||||
|
||||
def index
|
||||
|
@ -49,6 +51,8 @@ class Settings::ImportsController < Settings::BaseController
|
|||
csv << [row.data['domain']]
|
||||
when :bookmarks
|
||||
csv << [row.data['uri']]
|
||||
when :lists
|
||||
csv << [row.data['list_name'], row.data['acct']]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,10 +5,6 @@ module SettingsHelper
|
|||
LanguagesHelper::SUPPORTED_LOCALES.keys
|
||||
end
|
||||
|
||||
def hash_to_object(hash)
|
||||
HashObject.new(hash)
|
||||
end
|
||||
|
||||
def session_device_icon(session)
|
||||
device = session.detection.device
|
||||
|
||||
|
|
|
@ -143,7 +143,7 @@ class Account extends ImmutablePureComponent {
|
|||
const firstVerifiedField = account.get('fields').find(item => !!item.get('verified_at'));
|
||||
|
||||
if (firstVerifiedField) {
|
||||
verification = <>· <VerifiedBadge link={firstVerifiedField.get('value')} /></>;
|
||||
verification = <VerifiedBadge link={firstVerifiedField.get('value')} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -154,9 +154,13 @@ class Account extends ImmutablePureComponent {
|
|||
<Avatar account={account} size={size} />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className='account__contents'>
|
||||
<DisplayName account={account} />
|
||||
{!minimal && <><ShortNumber value={account.get('followers_count')} renderer={counterRenderer('followers')} /> {verification} {muteTimeRemaining}</>}
|
||||
{!minimal && (
|
||||
<div className='account__details'>
|
||||
<ShortNumber value={account.get('followers_count')} renderer={counterRenderer('followers')} /> {verification} {muteTimeRemaining}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
|
|
|
@ -57,9 +57,9 @@ class Poll extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
static getDerivedStateFromProps (props, state) {
|
||||
const { poll, intl } = props;
|
||||
const { poll } = props;
|
||||
const expires_at = poll.get('expires_at');
|
||||
const expired = poll.get('expired') || expires_at !== null && (new Date(expires_at)).getTime() < intl.now();
|
||||
const expired = poll.get('expired') || expires_at !== null && (new Date(expires_at)).getTime() < Date.now();
|
||||
return (expired === state.expired) ? null : { expired };
|
||||
}
|
||||
|
||||
|
@ -76,10 +76,10 @@ class Poll extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
_setupTimer () {
|
||||
const { poll, intl } = this.props;
|
||||
const { poll } = this.props;
|
||||
clearTimeout(this._timer);
|
||||
if (!this.state.expired) {
|
||||
const delay = (new Date(poll.get('expires_at'))).getTime() - intl.now();
|
||||
const delay = (new Date(poll.get('expires_at'))).getTime() - Date.now();
|
||||
this._timer = setTimeout(() => {
|
||||
this.setState({ expired: true });
|
||||
}, delay);
|
||||
|
|
|
@ -7814,13 +7814,28 @@ noscript {
|
|||
}
|
||||
}
|
||||
|
||||
.account__contents {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.account__details {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: 1em;
|
||||
}
|
||||
|
||||
.verified-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
color: $valid-value-color;
|
||||
gap: 4px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
> span {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class HashObject
|
||||
def initialize(hash)
|
||||
hash.each do |k, v|
|
||||
instance_variable_set("@#{k}", v)
|
||||
self.class.send(:define_method, k, proc { instance_variable_get("@#{k}") })
|
||||
end
|
||||
end
|
||||
end
|
|
@ -30,6 +30,7 @@ class BulkImport < ApplicationRecord
|
|||
muting: 2,
|
||||
domain_blocking: 3,
|
||||
bookmarks: 4,
|
||||
lists: 5,
|
||||
}
|
||||
|
||||
enum state: {
|
||||
|
|
|
@ -18,6 +18,7 @@ class Form::Import
|
|||
muting: ['Account address', 'Hide notifications'],
|
||||
domain_blocking: ['#domain'],
|
||||
bookmarks: ['#uri'],
|
||||
lists: ['List name', 'Account address'],
|
||||
}.freeze
|
||||
|
||||
KNOWN_FIRST_HEADERS = EXPECTED_HEADERS_BY_TYPE.values.map(&:first).uniq.freeze
|
||||
|
@ -30,6 +31,7 @@ class Form::Import
|
|||
'Hide notifications' => 'hide_notifications',
|
||||
'#domain' => 'domain',
|
||||
'#uri' => 'uri',
|
||||
'List name' => 'list_name',
|
||||
}.freeze
|
||||
|
||||
class EmptyFileError < StandardError; end
|
||||
|
@ -48,6 +50,7 @@ class Form::Import
|
|||
return :muting if data.original_filename&.start_with?('mutes') || data.original_filename&.start_with?('muted_accounts')
|
||||
return :domain_blocking if data.original_filename&.start_with?('domain_blocks') || data.original_filename&.start_with?('blocked_domains')
|
||||
return :bookmarks if data.original_filename&.start_with?('bookmarks')
|
||||
return :lists if data.original_filename&.start_with?('lists')
|
||||
end
|
||||
|
||||
# Whether the uploaded CSV file seems to correspond to a different import type than the one selected
|
||||
|
@ -76,14 +79,16 @@ class Form::Import
|
|||
|
||||
private
|
||||
|
||||
def default_csv_header
|
||||
def default_csv_headers
|
||||
case type.to_sym
|
||||
when :following, :blocking, :muting
|
||||
'Account address'
|
||||
['Account address']
|
||||
when :domain_blocking
|
||||
'#domain'
|
||||
['#domain']
|
||||
when :bookmarks
|
||||
'#uri'
|
||||
['#uri']
|
||||
when :lists
|
||||
['List name', 'Account address']
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -98,7 +103,7 @@ class Form::Import
|
|||
field&.split(',')&.map(&:strip)&.presence
|
||||
when 'Account address'
|
||||
field.strip.gsub(/\A@/, '')
|
||||
when '#domain', '#uri'
|
||||
when '#domain', '#uri', 'List name'
|
||||
field.strip
|
||||
else
|
||||
field
|
||||
|
@ -109,7 +114,7 @@ class Form::Import
|
|||
@csv_data.take(1) # Ensure the headers are read
|
||||
raise EmptyFileError if @csv_data.headers == true
|
||||
|
||||
@csv_data = CSV.open(data.path, encoding: 'UTF-8', skip_blanks: true, headers: [default_csv_header], converters: csv_converter) unless KNOWN_FIRST_HEADERS.include?(@csv_data.headers&.first)
|
||||
@csv_data = CSV.open(data.path, encoding: 'UTF-8', skip_blanks: true, headers: default_csv_headers, converters: csv_converter) unless KNOWN_FIRST_HEADERS.include?(@csv_data.headers&.first)
|
||||
@csv_data
|
||||
end
|
||||
|
||||
|
@ -133,7 +138,7 @@ class Form::Import
|
|||
def validate_data
|
||||
return if data.nil?
|
||||
return errors.add(:data, I18n.t('imports.errors.too_large')) if data.size > FILE_SIZE_LIMIT
|
||||
return errors.add(:data, I18n.t('imports.errors.incompatible_type')) unless csv_data.headers.include?(default_csv_header)
|
||||
return errors.add(:data, I18n.t('imports.errors.incompatible_type')) unless default_csv_headers.all? { |header| csv_data.headers.include?(header) }
|
||||
|
||||
errors.add(:data, I18n.t('imports.errors.over_rows_processing_limit', count: ROWS_PROCESSING_LIMIT)) if csv_row_count > ROWS_PROCESSING_LIMIT
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ class BulkImportRowService
|
|||
@type = row.bulk_import.type.to_sym
|
||||
|
||||
case @type
|
||||
when :following, :blocking, :muting
|
||||
when :following, :blocking, :muting, :lists
|
||||
target_acct = @data['acct']
|
||||
target_domain = domain(target_acct)
|
||||
@target_account = stoplight_wrap_request(target_domain) { ResolveAccountService.new.call(target_acct, { check_delivery_availability: true }) }
|
||||
|
@ -33,6 +33,12 @@ class BulkImportRowService
|
|||
return false unless StatusPolicy.new(@account, @target_status).show?
|
||||
|
||||
@account.bookmarks.find_or_create_by!(status: @target_status)
|
||||
when :lists
|
||||
list = @account.owned_lists.find_or_create_by!(title: @data['list_name'])
|
||||
|
||||
FollowService.new.call(@account, @target_account) unless @account.id == @target_account.id
|
||||
|
||||
list.accounts << @target_account
|
||||
end
|
||||
|
||||
true
|
||||
|
|
|
@ -16,6 +16,8 @@ class BulkImportService < BaseService
|
|||
import_domain_blocks!
|
||||
when :bookmarks
|
||||
import_bookmarks!
|
||||
when :lists
|
||||
import_lists!
|
||||
end
|
||||
|
||||
@import.update!(state: :finished, finished_at: Time.now.utc) if @import.processed_items == @import.total_items
|
||||
|
@ -157,4 +159,24 @@ class BulkImportService < BaseService
|
|||
[row.id]
|
||||
end
|
||||
end
|
||||
|
||||
def import_lists!
|
||||
rows = @import.rows.to_a
|
||||
|
||||
if @import.overwrite?
|
||||
included_lists = rows.map { |row| row.data['list_name'] }.uniq
|
||||
|
||||
@account.owned_lists.where.not(title: included_lists).destroy_all
|
||||
|
||||
# As list membership changes do not retroactively change timeline
|
||||
# contents, simplify things by just clearing everything
|
||||
@account.owned_lists.find_each do |list|
|
||||
list.list_accounts.destroy_all
|
||||
end
|
||||
end
|
||||
|
||||
Import::RowWorker.push_bulk(rows) do |row|
|
||||
[row.id]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
= simple_form_for @import, url: settings_imports_path do |f|
|
||||
.field-group
|
||||
= f.input :type, as: :grouped_select, collection: { constructive: %i(following bookmarks), destructive: %i(muting blocking domain_blocking) }, wrapper: :with_block_label, include_blank: false, label_method: ->(type) { I18n.t("imports.types.#{type}") }, group_label_method: ->(group) { I18n.t("imports.type_groups.#{group.first}") }, group_method: :last, hint: t('imports.preface')
|
||||
= f.input :type, as: :grouped_select, collection: { constructive: %i(following bookmarks lists), destructive: %i(muting blocking domain_blocking) }, wrapper: :with_block_label, include_blank: false, label_method: ->(type) { I18n.t("imports.types.#{type}") }, group_label_method: ->(group) { I18n.t("imports.type_groups.#{group.first}") }, group_method: :last, hint: t('imports.preface')
|
||||
|
||||
.fields-row
|
||||
.fields-group.fields-row__column.fields-row__column-6
|
||||
|
|
|
@ -25,7 +25,7 @@ module Twitter::TwitterText
|
|||
\)
|
||||
/iox
|
||||
UCHARS = '\u{A0}-\u{D7FF}\u{F900}-\u{FDCF}\u{FDF0}-\u{FFEF}\u{10000}-\u{1FFFD}\u{20000}-\u{2FFFD}\u{30000}-\u{3FFFD}\u{40000}-\u{4FFFD}\u{50000}-\u{5FFFD}\u{60000}-\u{6FFFD}\u{70000}-\u{7FFFD}\u{80000}-\u{8FFFD}\u{90000}-\u{9FFFD}\u{A0000}-\u{AFFFD}\u{B0000}-\u{BFFFD}\u{C0000}-\u{CFFFD}\u{D0000}-\u{DFFFD}\u{E1000}-\u{EFFFD}\u{E000}-\u{F8FF}\u{F0000}-\u{FFFFD}\u{100000}-\u{10FFFD}'
|
||||
REGEXEN[:valid_url_query_chars] = /[a-z0-9!?\*'\(\);:&=\+\$\/%#\[\]\-_\.,~|@#{UCHARS}]/iou
|
||||
REGEXEN[:valid_url_query_chars] = /[a-z0-9!?\*'\(\);:&=\+\$\/%#\[\]\-_\.,~|@\^#{UCHARS}]/iou
|
||||
REGEXEN[:valid_url_query_ending_chars] = /[a-z0-9_&=#\/\-#{UCHARS}]/iou
|
||||
REGEXEN[:valid_url_path] = /(?:
|
||||
(?:
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddPrimaryKeyToAccountsTagsJoinTable < ActiveRecord::Migration[6.1]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
ActiveRecord::Base.transaction do
|
||||
safety_assured do
|
||||
execute 'ALTER TABLE accounts_tags ADD PRIMARY KEY USING INDEX index_accounts_tags_on_tag_id_and_account_id'
|
||||
|
||||
# Rename for consistency as the primary key's name is not represented in db/schema.rb
|
||||
execute 'ALTER INDEX index_accounts_tags_on_tag_id_and_account_id RENAME TO accounts_tags_pkey'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
safety_assured do
|
||||
# I have found no way to demote the primary key to an index, instead, re-create the index
|
||||
execute 'CREATE UNIQUE INDEX CONCURRENTLY index_accounts_tags_on_tag_id_and_account_id ON accounts_tags (tag_id, account_id)'
|
||||
execute 'ALTER TABLE accounts_tags DROP CONSTRAINT accounts_tags_pkey'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,24 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddPrimaryKeyToStatusesTagsJoinTable < ActiveRecord::Migration[6.1]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
ActiveRecord::Base.transaction do
|
||||
safety_assured do
|
||||
execute 'ALTER TABLE statuses_tags ADD PRIMARY KEY USING INDEX index_statuses_tags_on_tag_id_and_status_id'
|
||||
|
||||
# Rename for consistency as the primary key's name is not represented in db/schema.rb
|
||||
execute 'ALTER INDEX index_statuses_tags_on_tag_id_and_status_id RENAME TO statuses_tags_pkey'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
safety_assured do
|
||||
# I have found no way to demote the primary key to an index, instead, re-create the index
|
||||
execute 'CREATE UNIQUE INDEX CONCURRENTLY index_statuses_tags_on_tag_id_and_status_id ON statuses_tags (tag_id, status_id)'
|
||||
execute 'ALTER TABLE statuses_tags DROP CONSTRAINT statuses_tags_pkey'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2023_05_24_194155) do
|
||||
ActiveRecord::Schema.define(version: 2023_05_31_154811) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
@ -194,11 +194,10 @@ ActiveRecord::Schema.define(version: 2023_05_24_194155) do
|
|||
t.index ["url"], name: "index_accounts_on_url", opclass: :text_pattern_ops, where: "(url IS NOT NULL)"
|
||||
end
|
||||
|
||||
create_table "accounts_tags", id: false, force: :cascade do |t|
|
||||
create_table "accounts_tags", primary_key: ["tag_id", "account_id"], force: :cascade do |t|
|
||||
t.bigint "account_id", null: false
|
||||
t.bigint "tag_id", null: false
|
||||
t.index ["account_id", "tag_id"], name: "index_accounts_tags_on_account_id_and_tag_id"
|
||||
t.index ["tag_id", "account_id"], name: "index_accounts_tags_on_tag_id_and_account_id", unique: true
|
||||
end
|
||||
|
||||
create_table "admin_action_logs", force: :cascade do |t|
|
||||
|
@ -982,11 +981,10 @@ ActiveRecord::Schema.define(version: 2023_05_24_194155) do
|
|||
t.index ["uri"], name: "index_statuses_on_uri", unique: true, opclass: :text_pattern_ops, where: "(uri IS NOT NULL)"
|
||||
end
|
||||
|
||||
create_table "statuses_tags", id: false, force: :cascade do |t|
|
||||
create_table "statuses_tags", primary_key: ["tag_id", "status_id"], force: :cascade do |t|
|
||||
t.bigint "status_id", null: false
|
||||
t.bigint "tag_id", null: false
|
||||
t.index ["status_id"], name: "index_statuses_tags_on_status_id"
|
||||
t.index ["tag_id", "status_id"], name: "index_statuses_tags_on_tag_id_and_status_id", unique: true
|
||||
end
|
||||
|
||||
create_table "system_keys", force: :cascade do |t|
|
||||
|
|
|
@ -4,16 +4,39 @@ require_relative '../../../config/boot'
|
|||
require_relative '../../../config/environment'
|
||||
|
||||
require 'thor'
|
||||
require_relative 'helper'
|
||||
require_relative 'progress_helper'
|
||||
|
||||
module Mastodon
|
||||
module CLI
|
||||
class Base < Thor
|
||||
include CLI::Helper
|
||||
include ProgressHelper
|
||||
|
||||
def self.exit_on_failure?
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def pastel
|
||||
@pastel ||= Pastel.new
|
||||
end
|
||||
|
||||
def dry_run?
|
||||
options[:dry_run]
|
||||
end
|
||||
|
||||
def dry_run_mode_suffix
|
||||
dry_run? ? ' (DRY RUN)' : ''
|
||||
end
|
||||
|
||||
def reset_connection_pools!
|
||||
ActiveRecord::Base.establish_connection(
|
||||
ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).first.configuration_hash
|
||||
.dup
|
||||
.tap { |config| config['pool'] = options[:concurrency] + 1 }
|
||||
)
|
||||
RedisConfiguration.establish_pool(options[:concurrency])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,23 +9,19 @@ HttpLog.configuration.logger = dev_null
|
|||
Paperclip.options[:log] = false
|
||||
Chewy.logger = dev_null
|
||||
|
||||
module Mastodon::CLI
|
||||
module Helper
|
||||
def dry_run?
|
||||
options[:dry_run]
|
||||
end
|
||||
require 'ruby-progressbar/outputs/null'
|
||||
|
||||
def dry_run_mode_suffix
|
||||
dry_run? ? ' (DRY RUN)' : ''
|
||||
end
|
||||
module Mastodon::CLI
|
||||
module ProgressHelper
|
||||
PROGRESS_FORMAT = '%c/%u |%b%i| %e'
|
||||
|
||||
def create_progress_bar(total = nil)
|
||||
ProgressBar.create(total: total, format: '%c/%u |%b%i| %e')
|
||||
end
|
||||
|
||||
def reset_connection_pools!
|
||||
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[Rails.env].dup.tap { |config| config['pool'] = options[:concurrency] + 1 })
|
||||
RedisConfiguration.establish_pool(options[:concurrency])
|
||||
ProgressBar.create(
|
||||
{
|
||||
total: total,
|
||||
format: PROGRESS_FORMAT,
|
||||
}.merge(progress_output_options)
|
||||
)
|
||||
end
|
||||
|
||||
def parallelize_with_progress(scope)
|
||||
|
@ -82,8 +78,10 @@ module Mastodon::CLI
|
|||
[total.value, aggregate.value]
|
||||
end
|
||||
|
||||
def pastel
|
||||
@pastel ||= Pastel.new
|
||||
private
|
||||
|
||||
def progress_output_options
|
||||
Rails.env.test? ? { output: ProgressBar::Outputs::Null } : {}
|
||||
end
|
||||
end
|
||||
end
|
|
@ -29,15 +29,7 @@ module Mastodon::CLI
|
|||
database will be imported into the indices, unless overridden with --no-import.
|
||||
LONG_DESC
|
||||
def deploy
|
||||
if options[:concurrency] < 1
|
||||
say('Cannot run with this concurrency setting, must be at least 1', :red)
|
||||
exit(1)
|
||||
end
|
||||
|
||||
if options[:batch_size] < 1
|
||||
say('Cannot run with this batch_size setting, must be at least 1', :red)
|
||||
exit(1)
|
||||
end
|
||||
verify_deploy_options!
|
||||
|
||||
indices = if options[:only]
|
||||
options[:only].map { |str| "#{str.camelize}Index".constantize }
|
||||
|
@ -98,5 +90,26 @@ module Mastodon::CLI
|
|||
|
||||
say("Indexed #{added} records, de-indexed #{removed}", :green, true)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def verify_deploy_options!
|
||||
verify_deploy_concurrency!
|
||||
verify_deploy_batch_size!
|
||||
end
|
||||
|
||||
def verify_deploy_concurrency!
|
||||
return unless options[:concurrency] < 1
|
||||
|
||||
say('Cannot run with this concurrency setting, must be at least 1', :red)
|
||||
exit(1)
|
||||
end
|
||||
|
||||
def verify_deploy_batch_size!
|
||||
return unless options[:batch_size] < 1
|
||||
|
||||
say('Cannot run with this batch_size setting, must be at least 1', :red)
|
||||
exit(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
16
package.json
16
package.json
|
@ -140,12 +140,12 @@
|
|||
"webpack-cli": "^3.3.12",
|
||||
"webpack-merge": "^5.9.0",
|
||||
"wicg-inert": "^3.1.2",
|
||||
"workbox-expiration": "^6.6.0",
|
||||
"workbox-precaching": "^6.6.0",
|
||||
"workbox-routing": "^6.6.0",
|
||||
"workbox-strategies": "^6.6.0",
|
||||
"workbox-webpack-plugin": "^6.6.0",
|
||||
"workbox-window": "^6.6.0",
|
||||
"workbox-expiration": "^7.0.0",
|
||||
"workbox-precaching": "^7.0.0",
|
||||
"workbox-routing": "^7.0.0",
|
||||
"workbox-strategies": "^7.0.0",
|
||||
"workbox-webpack-plugin": "^7.0.0",
|
||||
"workbox-window": "^7.0.0",
|
||||
"ws": "^8.12.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -158,7 +158,7 @@
|
|||
"@types/express": "^4.17.17",
|
||||
"@types/http-link-header": "^1.0.3",
|
||||
"@types/intl": "^1.2.0",
|
||||
"@types/jest": "^29.5.1",
|
||||
"@types/jest": "^29.5.2",
|
||||
"@types/js-yaml": "^4.0.5",
|
||||
"@types/lodash": "^4.14.195",
|
||||
"@types/npmlog": "^4.1.4",
|
||||
|
@ -192,7 +192,7 @@
|
|||
"eslint-import-resolver-typescript": "^3.5.5",
|
||||
"eslint-plugin-formatjs": "^4.10.1",
|
||||
"eslint-plugin-import": "~2.27.5",
|
||||
"eslint-plugin-jsdoc": "^45.0.0",
|
||||
"eslint-plugin-jsdoc": "^46.1.0",
|
||||
"eslint-plugin-jsx-a11y": "~6.7.1",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-promise": "~6.1.1",
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
Mastodon project,gargron@example.com
|
||||
Mastodon project,mastodon@example.com
|
||||
test,foo@example.com
|
|
|
@ -1,9 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe HashObject do
|
||||
it 'has methods corresponding to hash properties' do
|
||||
expect(HashObject.new(key: 'value').key).to eq 'value'
|
||||
end
|
||||
end
|
|
@ -662,4 +662,340 @@ describe Mastodon::CLI::Accounts do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#refresh' do
|
||||
context 'with --all option' do
|
||||
let!(:local_account) { Fabricate(:account, domain: nil) }
|
||||
let!(:remote_account_example_com) { Fabricate(:account, domain: 'example.com') }
|
||||
let!(:account_example_net) { Fabricate(:account, domain: 'example.net') }
|
||||
let(:scope) { Account.remote }
|
||||
|
||||
before do
|
||||
allow(cli).to receive(:parallelize_with_progress).and_yield(remote_account_example_com)
|
||||
.and_yield(account_example_net)
|
||||
.and_return([2, nil])
|
||||
cli.options = { all: true }
|
||||
end
|
||||
|
||||
it 'refreshes the avatar for all remote accounts' do
|
||||
allow(remote_account_example_com).to receive(:reset_avatar!)
|
||||
allow(account_example_net).to receive(:reset_avatar!)
|
||||
|
||||
cli.refresh
|
||||
|
||||
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
|
||||
expect(remote_account_example_com).to have_received(:reset_avatar!).once
|
||||
expect(account_example_net).to have_received(:reset_avatar!).once
|
||||
end
|
||||
|
||||
it 'does not refresh avatar for local accounts' do
|
||||
allow(local_account).to receive(:reset_avatar!)
|
||||
|
||||
cli.refresh
|
||||
|
||||
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
|
||||
expect(local_account).to_not have_received(:reset_avatar!)
|
||||
end
|
||||
|
||||
it 'refreshes the header for all remote accounts' do
|
||||
allow(remote_account_example_com).to receive(:reset_header!)
|
||||
allow(account_example_net).to receive(:reset_header!)
|
||||
|
||||
cli.refresh
|
||||
|
||||
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
|
||||
expect(remote_account_example_com).to have_received(:reset_header!).once
|
||||
expect(account_example_net).to have_received(:reset_header!).once
|
||||
end
|
||||
|
||||
it 'does not refresh the header for local accounts' do
|
||||
allow(local_account).to receive(:reset_header!)
|
||||
|
||||
cli.refresh
|
||||
|
||||
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
|
||||
expect(local_account).to_not have_received(:reset_header!)
|
||||
end
|
||||
|
||||
it 'displays a successful message' do
|
||||
expect { cli.refresh }.to output(
|
||||
a_string_including('Refreshed 2 accounts')
|
||||
).to_stdout
|
||||
end
|
||||
|
||||
context 'with --dry-run option' do
|
||||
before do
|
||||
cli.options = { all: true, dry_run: true }
|
||||
end
|
||||
|
||||
it 'does not refresh the avatar for any account' do
|
||||
allow(local_account).to receive(:reset_avatar!)
|
||||
allow(remote_account_example_com).to receive(:reset_avatar!)
|
||||
allow(account_example_net).to receive(:reset_avatar!)
|
||||
|
||||
cli.refresh
|
||||
|
||||
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
|
||||
expect(local_account).to_not have_received(:reset_avatar!)
|
||||
expect(remote_account_example_com).to_not have_received(:reset_avatar!)
|
||||
expect(account_example_net).to_not have_received(:reset_avatar!)
|
||||
end
|
||||
|
||||
it 'does not refresh the header for any account' do
|
||||
allow(local_account).to receive(:reset_header!)
|
||||
allow(remote_account_example_com).to receive(:reset_header!)
|
||||
allow(account_example_net).to receive(:reset_header!)
|
||||
|
||||
cli.refresh
|
||||
|
||||
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
|
||||
expect(local_account).to_not have_received(:reset_header!)
|
||||
expect(remote_account_example_com).to_not have_received(:reset_header!)
|
||||
expect(account_example_net).to_not have_received(:reset_header!)
|
||||
end
|
||||
|
||||
it 'displays a successful message with (DRY RUN)' do
|
||||
expect { cli.refresh }.to output(
|
||||
a_string_including('Refreshed 2 accounts (DRY RUN)')
|
||||
).to_stdout
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a list of accts' do
|
||||
let!(:account_example_com_a) { Fabricate(:account, domain: 'example.com') }
|
||||
let!(:account_example_com_b) { Fabricate(:account, domain: 'example.com') }
|
||||
let!(:account_example_net) { Fabricate(:account, domain: 'example.net') }
|
||||
let(:arguments) { [account_example_com_a.acct, account_example_com_b.acct] }
|
||||
|
||||
before do
|
||||
allow(Account).to receive(:find_remote).with(account_example_com_a.username, account_example_com_a.domain).and_return(account_example_com_a)
|
||||
allow(Account).to receive(:find_remote).with(account_example_com_b.username, account_example_com_b.domain).and_return(account_example_com_b)
|
||||
allow(Account).to receive(:find_remote).with(account_example_net.username, account_example_net.domain).and_return(account_example_net)
|
||||
end
|
||||
|
||||
it 'resets the avatar for the specified accounts' do
|
||||
allow(account_example_com_a).to receive(:reset_avatar!)
|
||||
allow(account_example_com_b).to receive(:reset_avatar!)
|
||||
|
||||
cli.refresh(*arguments)
|
||||
|
||||
expect(account_example_com_a).to have_received(:reset_avatar!).once
|
||||
expect(account_example_com_b).to have_received(:reset_avatar!).once
|
||||
end
|
||||
|
||||
it 'does not reset the avatar for unspecified accounts' do
|
||||
allow(account_example_net).to receive(:reset_avatar!)
|
||||
|
||||
cli.refresh(*arguments)
|
||||
|
||||
expect(account_example_net).to_not have_received(:reset_avatar!)
|
||||
end
|
||||
|
||||
it 'resets the header for the specified accounts' do
|
||||
allow(account_example_com_a).to receive(:reset_header!)
|
||||
allow(account_example_com_b).to receive(:reset_header!)
|
||||
|
||||
cli.refresh(*arguments)
|
||||
|
||||
expect(account_example_com_a).to have_received(:reset_header!).once
|
||||
expect(account_example_com_b).to have_received(:reset_header!).once
|
||||
end
|
||||
|
||||
it 'does not reset the header for unspecified accounts' do
|
||||
allow(account_example_net).to receive(:reset_header!)
|
||||
|
||||
cli.refresh(*arguments)
|
||||
|
||||
expect(account_example_net).to_not have_received(:reset_header!)
|
||||
end
|
||||
|
||||
context 'when an UnexpectedResponseError is raised' do
|
||||
it 'displays a failure message' do
|
||||
allow(account_example_com_a).to receive(:reset_avatar!).and_raise(Mastodon::UnexpectedResponseError)
|
||||
|
||||
expect { cli.refresh(*arguments) }
|
||||
.to output(
|
||||
a_string_including("Account failed: #{account_example_com_a.username}@#{account_example_com_a.domain}")
|
||||
).to_stdout
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a specified account is not found' do
|
||||
it 'exits with an error message' do
|
||||
allow(Account).to receive(:find_remote).with(account_example_com_b.username, account_example_com_b.domain).and_return(nil)
|
||||
|
||||
expect { cli.refresh(*arguments) }.to output(
|
||||
a_string_including('No such account')
|
||||
).to_stdout
|
||||
.and raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with --dry-run option' do
|
||||
before do
|
||||
cli.options = { dry_run: true }
|
||||
end
|
||||
|
||||
it 'does not refresh the avatar for any account' do
|
||||
allow(account_example_com_a).to receive(:reset_avatar!)
|
||||
allow(account_example_com_b).to receive(:reset_avatar!)
|
||||
|
||||
cli.refresh(*arguments)
|
||||
|
||||
expect(account_example_com_a).to_not have_received(:reset_avatar!)
|
||||
expect(account_example_com_b).to_not have_received(:reset_avatar!)
|
||||
end
|
||||
|
||||
it 'does not refresh the header for any account' do
|
||||
allow(account_example_com_a).to receive(:reset_header!)
|
||||
allow(account_example_com_b).to receive(:reset_header!)
|
||||
|
||||
cli.refresh(*arguments)
|
||||
|
||||
expect(account_example_com_a).to_not have_received(:reset_header!)
|
||||
expect(account_example_com_b).to_not have_received(:reset_header!)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with --domain option' do
|
||||
let!(:account_example_com_a) { Fabricate(:account, domain: 'example.com') }
|
||||
let!(:account_example_com_b) { Fabricate(:account, domain: 'example.com') }
|
||||
let!(:account_example_net) { Fabricate(:account, domain: 'example.net') }
|
||||
let(:domain) { 'example.com' }
|
||||
let(:scope) { Account.remote.where(domain: domain) }
|
||||
|
||||
before do
|
||||
allow(cli).to receive(:parallelize_with_progress).and_yield(account_example_com_a)
|
||||
.and_yield(account_example_com_b)
|
||||
.and_return([2, nil])
|
||||
|
||||
cli.options = { domain: domain }
|
||||
end
|
||||
|
||||
it 'refreshes the avatar for all accounts on specified domain' do
|
||||
allow(account_example_com_a).to receive(:reset_avatar!)
|
||||
allow(account_example_com_b).to receive(:reset_avatar!)
|
||||
|
||||
cli.refresh
|
||||
|
||||
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
|
||||
expect(account_example_com_a).to have_received(:reset_avatar!).once
|
||||
expect(account_example_com_b).to have_received(:reset_avatar!).once
|
||||
end
|
||||
|
||||
it 'does not refresh the avatar for accounts outside specified domain' do
|
||||
allow(account_example_net).to receive(:reset_avatar!)
|
||||
|
||||
cli.refresh
|
||||
|
||||
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
|
||||
expect(account_example_net).to_not have_received(:reset_avatar!)
|
||||
end
|
||||
|
||||
it 'refreshes the header for all accounts on specified domain' do
|
||||
allow(account_example_com_a).to receive(:reset_header!)
|
||||
allow(account_example_com_b).to receive(:reset_header!)
|
||||
|
||||
cli.refresh
|
||||
|
||||
expect(cli).to have_received(:parallelize_with_progress).with(scope)
|
||||
expect(account_example_com_a).to have_received(:reset_header!).once
|
||||
expect(account_example_com_b).to have_received(:reset_header!).once
|
||||
end
|
||||
|
||||
it 'does not refresh the header for accounts outside specified domain' do
|
||||
allow(account_example_net).to receive(:reset_header!)
|
||||
|
||||
cli.refresh
|
||||
|
||||
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
|
||||
expect(account_example_net).to_not have_received(:reset_header!)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when neither a list of accts nor options are provided' do
|
||||
it 'exits with an error message' do
|
||||
expect { cli.refresh }.to output(
|
||||
a_string_including('No account(s) given')
|
||||
).to_stdout
|
||||
.and raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#rotate' do
|
||||
context 'when neither username nor --all option are given' do
|
||||
it 'exits with an error message' do
|
||||
expect { cli.rotate }.to output(
|
||||
a_string_including('No account(s) given')
|
||||
).to_stdout
|
||||
.and raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a username is given' do
|
||||
let(:account) { Fabricate(:account) }
|
||||
|
||||
it 'correctly rotates keys for the specified account' do
|
||||
old_private_key = account.private_key
|
||||
old_public_key = account.public_key
|
||||
|
||||
cli.rotate(account.username)
|
||||
account.reload
|
||||
|
||||
expect(account.private_key).to_not eq(old_private_key)
|
||||
expect(account.public_key).to_not eq(old_public_key)
|
||||
end
|
||||
|
||||
it 'broadcasts the new keys for the specified account' do
|
||||
allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_in)
|
||||
|
||||
cli.rotate(account.username)
|
||||
|
||||
expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_in).with(anything, account.id, anything).once
|
||||
end
|
||||
|
||||
context 'when the given username is not found' do
|
||||
it 'exits with an error message when the specified username is not found' do
|
||||
expect { cli.rotate('non_existent_username') }.to output(
|
||||
a_string_including('No such account')
|
||||
).to_stdout
|
||||
.and raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when --all option is provided' do
|
||||
let(:accounts) { Fabricate.times(3, :account) }
|
||||
let(:options) { { all: true } }
|
||||
|
||||
before do
|
||||
allow(Account).to receive(:local).and_return(Account.where(id: accounts.map(&:id)))
|
||||
cli.options = { all: true }
|
||||
end
|
||||
|
||||
it 'correctly rotates keys for all local accounts' do
|
||||
old_private_keys = accounts.map(&:private_key)
|
||||
old_public_keys = accounts.map(&:public_key)
|
||||
|
||||
cli.rotate
|
||||
accounts.each(&:reload)
|
||||
|
||||
expect(accounts.map(&:private_key)).to_not eq(old_private_keys)
|
||||
expect(accounts.map(&:public_key)).to_not eq(old_public_keys)
|
||||
end
|
||||
|
||||
it 'broadcasts the new keys for each account' do
|
||||
allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_in)
|
||||
|
||||
cli.rotate
|
||||
|
||||
accounts.each do |account|
|
||||
expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_in).with(anything, account.id, anything).once
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -86,6 +86,7 @@ RSpec.describe Form::Import do
|
|||
it_behaves_like 'too many CSV rows', 'muting', 'imports.txt', 1
|
||||
it_behaves_like 'too many CSV rows', 'domain_blocking', 'domain_blocks.csv', 2
|
||||
it_behaves_like 'too many CSV rows', 'bookmarks', 'bookmark-imports.txt', 3
|
||||
it_behaves_like 'too many CSV rows', 'lists', 'lists.csv', 2
|
||||
|
||||
# Importing list of addresses with no headers into various types
|
||||
it_behaves_like 'valid import', 'following', 'imports.txt'
|
||||
|
@ -98,6 +99,9 @@ RSpec.describe Form::Import do
|
|||
# Importing bookmarks list with no headers into expected type
|
||||
it_behaves_like 'valid import', 'bookmarks', 'bookmark-imports.txt'
|
||||
|
||||
# Importing lists with no headers into expected type
|
||||
it_behaves_like 'valid import', 'lists', 'lists.csv'
|
||||
|
||||
# Importing followed accounts with headers into various compatible types
|
||||
it_behaves_like 'valid import', 'following', 'following_accounts.csv'
|
||||
it_behaves_like 'valid import', 'blocking', 'following_accounts.csv'
|
||||
|
@ -273,6 +277,12 @@ RSpec.describe Form::Import do
|
|||
{ 'acct' => 'user@test.com', 'hide_notifications' => false },
|
||||
]
|
||||
|
||||
it_behaves_like 'on successful import', 'lists', 'merge', 'lists.csv', [
|
||||
{ 'acct' => 'gargron@example.com', 'list_name' => 'Mastodon project' },
|
||||
{ 'acct' => 'mastodon@example.com', 'list_name' => 'Mastodon project' },
|
||||
{ 'acct' => 'foo@example.com', 'list_name' => 'test' },
|
||||
]
|
||||
|
||||
# Based on the bug report 20571 where UTF-8 encoded domains were rejecting import of their users
|
||||
#
|
||||
# https://github.com/mastodon/mastodon/issues/20571
|
||||
|
|
|
@ -91,5 +91,77 @@ RSpec.describe BulkImportRowService do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when importing a list row' do
|
||||
let(:import_type) { 'lists' }
|
||||
let(:target_account) { Fabricate(:account) }
|
||||
let(:data) do
|
||||
{ 'acct' => target_account.acct, 'list_name' => 'my list' }
|
||||
end
|
||||
|
||||
shared_examples 'common behavior' do
|
||||
context 'when the target account is already followed' do
|
||||
before do
|
||||
account.follow!(target_account)
|
||||
end
|
||||
|
||||
it 'returns true' do
|
||||
expect(subject.call(import_row)).to be true
|
||||
end
|
||||
|
||||
it 'adds the target account to the list' do
|
||||
expect { subject.call(import_row) }.to change { ListAccount.joins(:list).exists?(account_id: target_account.id, list: { title: 'my list' }) }.from(false).to(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the user already requested to follow the target account' do
|
||||
before do
|
||||
account.request_follow!(target_account)
|
||||
end
|
||||
|
||||
it 'returns true' do
|
||||
expect(subject.call(import_row)).to be true
|
||||
end
|
||||
|
||||
it 'adds the target account to the list' do
|
||||
expect { subject.call(import_row) }.to change { ListAccount.joins(:list).exists?(account_id: target_account.id, list: { title: 'my list' }) }.from(false).to(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the target account is neither followed nor requested' do
|
||||
it 'returns true' do
|
||||
expect(subject.call(import_row)).to be true
|
||||
end
|
||||
|
||||
it 'adds the target account to the list' do
|
||||
expect { subject.call(import_row) }.to change { ListAccount.joins(:list).exists?(account_id: target_account.id, list: { title: 'my list' }) }.from(false).to(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the target account is the user themself' do
|
||||
let(:target_account) { account }
|
||||
|
||||
it 'returns true' do
|
||||
expect(subject.call(import_row)).to be true
|
||||
end
|
||||
|
||||
it 'adds the target account to the list' do
|
||||
expect { subject.call(import_row) }.to change { ListAccount.joins(:list).exists?(account_id: target_account.id, list: { title: 'my list' }) }.from(false).to(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the list does not exist yet' do
|
||||
include_examples 'common behavior'
|
||||
end
|
||||
|
||||
context 'when the list exists' do
|
||||
before do
|
||||
Fabricate(:list, account: account, title: 'my list')
|
||||
end
|
||||
|
||||
include_examples 'common behavior'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,6 +12,7 @@ RSpec.describe FetchLinkCardService, type: :service do
|
|||
stub_request(:get, 'http://example.com/koi8-r').to_return(request_fixture('koi8-r.txt'))
|
||||
stub_request(:get, 'http://example.com/日本語').to_return(request_fixture('sjis.txt'))
|
||||
stub_request(:get, 'https://github.com/qbi/WannaCry').to_return(status: 404)
|
||||
stub_request(:get, 'http://example.com/test?data=file.gpx%5E1').to_return(status: 200)
|
||||
stub_request(:get, 'http://example.com/test-').to_return(request_fixture('idn.txt'))
|
||||
stub_request(:get, 'http://example.com/windows-1251').to_return(request_fixture('windows-1251.txt'))
|
||||
|
||||
|
@ -87,6 +88,15 @@ RSpec.describe FetchLinkCardService, type: :service do
|
|||
expect(a_request(:get, 'http://example.com/sjis')).to_not have_been_made
|
||||
end
|
||||
end
|
||||
|
||||
context do
|
||||
let(:status) { Fabricate(:status, text: 'test http://example.com/test?data=file.gpx^1') }
|
||||
|
||||
it 'does fetch URLs with a caret in search params' do
|
||||
expect(a_request(:get, 'http://example.com/test?data=file.gpx')).to_not have_been_made
|
||||
expect(a_request(:get, 'http://example.com/test?data=file.gpx%5E1')).to have_been_made.once
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a remote status' do
|
||||
|
|
283
yarn.lock
283
yarn.lock
|
@ -1743,6 +1743,15 @@
|
|||
"@jridgewell/set-array" "^1.0.0"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||
|
||||
"@jridgewell/gen-mapping@^0.3.0":
|
||||
version "0.3.3"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098"
|
||||
integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==
|
||||
dependencies:
|
||||
"@jridgewell/set-array" "^1.0.1"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||
"@jridgewell/trace-mapping" "^0.3.9"
|
||||
|
||||
"@jridgewell/gen-mapping@^0.3.2":
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9"
|
||||
|
@ -1767,6 +1776,14 @@
|
|||
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
|
||||
integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
|
||||
|
||||
"@jridgewell/source-map@^0.3.2":
|
||||
version "0.3.3"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.3.tgz#8108265659d4c33e72ffe14e33d6cc5eb59f2fda"
|
||||
integrity sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==
|
||||
dependencies:
|
||||
"@jridgewell/gen-mapping" "^0.3.0"
|
||||
"@jridgewell/trace-mapping" "^0.3.9"
|
||||
|
||||
"@jridgewell/sourcemap-codec@1.4.14":
|
||||
version "1.4.14"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
|
||||
|
@ -2231,10 +2248,10 @@
|
|||
dependencies:
|
||||
"@types/istanbul-lib-report" "*"
|
||||
|
||||
"@types/jest@*", "@types/jest@^29.5.1":
|
||||
version "29.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.1.tgz#83c818aa9a87da27d6da85d3378e5a34d2f31a47"
|
||||
integrity sha512-tEuVcHrpaixS36w7hpsfLBLpjtMRJUE09/MHXn923LOVojDwyC14cWcfc0rDs0VEfUyYmt/+iX1kxxp+gZMcaQ==
|
||||
"@types/jest@*", "@types/jest@^29.5.2":
|
||||
version "29.5.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.2.tgz#86b4afc86e3a8f3005b297ed8a72494f89e6395b"
|
||||
integrity sha512-mSoZVJF5YzGVCk+FsDxzDuH7s+SCkzrgKZzf0Z0T2WudhBUPoF6ktoTPC4R0ZoCPCV5xUvuU6ias5NvxcBcMMg==
|
||||
dependencies:
|
||||
expect "^29.0.0"
|
||||
pretty-format "^29.0.0"
|
||||
|
@ -3007,9 +3024,9 @@ ansi-regex@^2.0.0:
|
|||
integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
|
||||
|
||||
ansi-regex@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
|
||||
integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed"
|
||||
integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==
|
||||
|
||||
ansi-regex@^5.0.0, ansi-regex@^5.0.1:
|
||||
version "5.0.1"
|
||||
|
@ -5264,10 +5281,10 @@ eslint-plugin-import@~2.27.5:
|
|||
semver "^6.3.0"
|
||||
tsconfig-paths "^3.14.1"
|
||||
|
||||
eslint-plugin-jsdoc@^45.0.0:
|
||||
version "45.0.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-45.0.0.tgz#6be84e4842a7138cc571a907ea9c31c42eaac5c0"
|
||||
integrity sha512-l2+Jcs/Ps7oFA+SWY+0sweU/e5LgricnEl6EsDlyRTF5y0+NWL1y9Qwz9PHwHAxtdJq6lxPjEQWmYLMkvhzD4g==
|
||||
eslint-plugin-jsdoc@^46.1.0:
|
||||
version "46.1.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.1.0.tgz#3ff932b70bc25f3745049f525a789faed7c948da"
|
||||
integrity sha512-NpjpSuWR+Wwxzmssji7AVty1Vu0JvI7v+cTj+Rw1nKVjGv2eMvLGM/SI4VpgTXp82JbLtFOsA2QYLHT3YSmASA==
|
||||
dependencies:
|
||||
"@es-joy/jsdoccomment" "~0.39.4"
|
||||
are-docs-informative "^0.0.2"
|
||||
|
@ -7860,9 +7877,9 @@ loader-utils@^1.2.3, loader-utils@^1.4.0:
|
|||
json5 "^1.0.1"
|
||||
|
||||
loader-utils@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0"
|
||||
integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c"
|
||||
integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==
|
||||
dependencies:
|
||||
big.js "^5.2.2"
|
||||
emojis-list "^3.0.0"
|
||||
|
@ -10782,7 +10799,7 @@ source-map@^0.7.3:
|
|||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656"
|
||||
integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==
|
||||
|
||||
source-map@^0.8.0-beta.0, source-map@~0.8.0-beta.0:
|
||||
source-map@^0.8.0-beta.0:
|
||||
version "0.8.0-beta.0"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.8.0-beta.0.tgz#d4c1bb42c3f7ee925f005927ba10709e0d1d1f11"
|
||||
integrity sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==
|
||||
|
@ -10863,9 +10880,9 @@ sprintf-js@~1.0.2:
|
|||
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
|
||||
|
||||
ssri@^8.0.0:
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.0.tgz#79ca74e21f8ceaeddfcb4b90143c458b8d988808"
|
||||
integrity sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA==
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af"
|
||||
integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==
|
||||
dependencies:
|
||||
minipass "^3.1.1"
|
||||
|
||||
|
@ -11359,13 +11376,13 @@ terser-webpack-plugin@^1.4.3, terser-webpack-plugin@^4.2.3:
|
|||
webpack-sources "^1.4.3"
|
||||
|
||||
terser@^5.0.0, terser@^5.3.4:
|
||||
version "5.13.1"
|
||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.13.1.tgz#66332cdc5a01b04a224c9fad449fc1a18eaa1799"
|
||||
integrity sha512-hn4WKOfwnwbYfe48NgrQjqNOH9jzLqRcIfbYytOXCOv46LBfWr9bDS17MQqOi+BWGD0sJK3Sj5NC/gJjiojaoA==
|
||||
version "5.17.6"
|
||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.17.6.tgz#d810e75e1bb3350c799cd90ebefe19c9412c12de"
|
||||
integrity sha512-V8QHcs8YuyLkLHsJO5ucyff1ykrLVsR4dNnS//L5Y3NiSXpbK1J+WMVUs67eI0KTxs9JtHhgEQpXQVHlHI92DQ==
|
||||
dependencies:
|
||||
"@jridgewell/source-map" "^0.3.2"
|
||||
acorn "^8.5.0"
|
||||
commander "^2.20.0"
|
||||
source-map "~0.8.0-beta.0"
|
||||
source-map-support "~0.5.20"
|
||||
|
||||
tesseract.js-core@^2.2.0:
|
||||
|
@ -12267,25 +12284,25 @@ word-wrap@^1.2.3, word-wrap@~1.2.3:
|
|||
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
|
||||
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
|
||||
|
||||
workbox-background-sync@6.6.1:
|
||||
version "6.6.1"
|
||||
resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-6.6.1.tgz#08d603a33717ce663e718c30cc336f74909aff2f"
|
||||
integrity sha512-trJd3ovpWCvzu4sW0E8rV3FUyIcC0W8G+AZ+VcqzzA890AsWZlUGOTSxIMmIHVusUw/FDq1HFWfy/kC/WTRqSg==
|
||||
workbox-background-sync@7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-7.0.0.tgz#2b84b96ca35fec976e3bd2794b70e4acec46b3a5"
|
||||
integrity sha512-S+m1+84gjdueM+jIKZ+I0Lx0BDHkk5Nu6a3kTVxP4fdj3gKouRNmhO8H290ybnJTOPfBDtTMXSQA/QLTvr7PeA==
|
||||
dependencies:
|
||||
idb "^7.0.1"
|
||||
workbox-core "6.6.1"
|
||||
workbox-core "7.0.0"
|
||||
|
||||
workbox-broadcast-update@6.6.1:
|
||||
version "6.6.1"
|
||||
resolved "https://registry.yarnpkg.com/workbox-broadcast-update/-/workbox-broadcast-update-6.6.1.tgz#0fad9454cf8e4ace0c293e5617c64c75d8a8c61e"
|
||||
integrity sha512-fBhffRdaANdeQ1V8s692R9l/gzvjjRtydBOvR6WCSB0BNE2BacA29Z4r9/RHd9KaXCPl6JTdI9q0bR25YKP8TQ==
|
||||
workbox-broadcast-update@7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/workbox-broadcast-update/-/workbox-broadcast-update-7.0.0.tgz#7f611ca1a94ba8ac0aa40fa171c9713e0f937d22"
|
||||
integrity sha512-oUuh4jzZrLySOo0tC0WoKiSg90bVAcnE98uW7F8GFiSOXnhogfNDGZelPJa+6KpGBO5+Qelv04Hqx2UD+BJqNQ==
|
||||
dependencies:
|
||||
workbox-core "6.6.1"
|
||||
workbox-core "7.0.0"
|
||||
|
||||
workbox-build@6.6.1:
|
||||
version "6.6.1"
|
||||
resolved "https://registry.yarnpkg.com/workbox-build/-/workbox-build-6.6.1.tgz#6010e9ce550910156761448f2dbea8cfcf759cb0"
|
||||
integrity sha512-INPgDx6aRycAugUixbKgiEQBWD0MPZqU5r0jyr24CehvNuLPSXp/wGOpdRJmts656lNiXwqV7dC2nzyrzWEDnw==
|
||||
workbox-build@7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/workbox-build/-/workbox-build-7.0.0.tgz#02ab5ef2991b3369b8b9395703f08912212769b4"
|
||||
integrity sha512-CttE7WCYW9sZC+nUYhQg3WzzGPr4IHmrPnjKiu3AMXsiNQKx+l4hHl63WTrnicLmKEKHScWDH8xsGBdrYgtBzg==
|
||||
dependencies:
|
||||
"@apideck/better-ajv-errors" "^0.3.1"
|
||||
"@babel/core" "^7.11.1"
|
||||
|
@ -12309,132 +12326,132 @@ workbox-build@6.6.1:
|
|||
strip-comments "^2.0.1"
|
||||
tempy "^0.6.0"
|
||||
upath "^1.2.0"
|
||||
workbox-background-sync "6.6.1"
|
||||
workbox-broadcast-update "6.6.1"
|
||||
workbox-cacheable-response "6.6.1"
|
||||
workbox-core "6.6.1"
|
||||
workbox-expiration "6.6.1"
|
||||
workbox-google-analytics "6.6.1"
|
||||
workbox-navigation-preload "6.6.1"
|
||||
workbox-precaching "6.6.1"
|
||||
workbox-range-requests "6.6.1"
|
||||
workbox-recipes "6.6.1"
|
||||
workbox-routing "6.6.1"
|
||||
workbox-strategies "6.6.1"
|
||||
workbox-streams "6.6.1"
|
||||
workbox-sw "6.6.1"
|
||||
workbox-window "6.6.1"
|
||||
workbox-background-sync "7.0.0"
|
||||
workbox-broadcast-update "7.0.0"
|
||||
workbox-cacheable-response "7.0.0"
|
||||
workbox-core "7.0.0"
|
||||
workbox-expiration "7.0.0"
|
||||
workbox-google-analytics "7.0.0"
|
||||
workbox-navigation-preload "7.0.0"
|
||||
workbox-precaching "7.0.0"
|
||||
workbox-range-requests "7.0.0"
|
||||
workbox-recipes "7.0.0"
|
||||
workbox-routing "7.0.0"
|
||||
workbox-strategies "7.0.0"
|
||||
workbox-streams "7.0.0"
|
||||
workbox-sw "7.0.0"
|
||||
workbox-window "7.0.0"
|
||||
|
||||
workbox-cacheable-response@6.6.1:
|
||||
version "6.6.1"
|
||||
resolved "https://registry.yarnpkg.com/workbox-cacheable-response/-/workbox-cacheable-response-6.6.1.tgz#284c2b86be3f4fd191970ace8c8e99797bcf58e9"
|
||||
integrity sha512-85LY4veT2CnTCDxaVG7ft3NKaFbH6i4urZXgLiU4AiwvKqS2ChL6/eILiGRYXfZ6gAwDnh5RkuDbr/GMS4KSag==
|
||||
workbox-cacheable-response@7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/workbox-cacheable-response/-/workbox-cacheable-response-7.0.0.tgz#ee27c036728189eed69d25a135013053277482d2"
|
||||
integrity sha512-0lrtyGHn/LH8kKAJVOQfSu3/80WDc9Ma8ng0p2i/5HuUndGttH+mGMSvOskjOdFImLs2XZIimErp7tSOPmu/6g==
|
||||
dependencies:
|
||||
workbox-core "6.6.1"
|
||||
workbox-core "7.0.0"
|
||||
|
||||
workbox-core@6.6.1:
|
||||
version "6.6.1"
|
||||
resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-6.6.1.tgz#7184776d4134c5ed2f086878c882728fc9084265"
|
||||
integrity sha512-ZrGBXjjaJLqzVothoE12qTbVnOAjFrHDXpZe7coCb6q65qI/59rDLwuFMO4PcZ7jcbxY+0+NhUVztzR/CbjEFw==
|
||||
workbox-core@7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-7.0.0.tgz#dec114ec923cc2adc967dd9be1b8a0bed50a3545"
|
||||
integrity sha512-81JkAAZtfVP8darBpfRTovHg8DGAVrKFgHpOArZbdFd78VqHr5Iw65f2guwjE2NlCFbPFDoez3D3/6ZvhI/rwQ==
|
||||
|
||||
workbox-expiration@6.6.1, workbox-expiration@^6.6.0:
|
||||
version "6.6.1"
|
||||
resolved "https://registry.yarnpkg.com/workbox-expiration/-/workbox-expiration-6.6.1.tgz#a841fa36676104426dbfb9da1ef6a630b4f93739"
|
||||
integrity sha512-qFiNeeINndiOxaCrd2DeL1Xh1RFug3JonzjxUHc5WkvkD2u5abY3gZL1xSUNt3vZKsFFGGORItSjVTVnWAZO4A==
|
||||
workbox-expiration@7.0.0, workbox-expiration@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/workbox-expiration/-/workbox-expiration-7.0.0.tgz#3d90bcf2a7577241de950f89784f6546b66c2baa"
|
||||
integrity sha512-MLK+fogW+pC3IWU9SFE+FRStvDVutwJMR5if1g7oBJx3qwmO69BNoJQVaMXq41R0gg3MzxVfwOGKx3i9P6sOLQ==
|
||||
dependencies:
|
||||
idb "^7.0.1"
|
||||
workbox-core "6.6.1"
|
||||
workbox-core "7.0.0"
|
||||
|
||||
workbox-google-analytics@6.6.1:
|
||||
version "6.6.1"
|
||||
resolved "https://registry.yarnpkg.com/workbox-google-analytics/-/workbox-google-analytics-6.6.1.tgz#a07a6655ab33d89d1b0b0a935ffa5dea88618c5d"
|
||||
integrity sha512-1TjSvbFSLmkpqLcBsF7FuGqqeDsf+uAXO/pjiINQKg3b1GN0nBngnxLcXDYo1n/XxK4N7RaRrpRlkwjY/3ocuA==
|
||||
workbox-google-analytics@7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/workbox-google-analytics/-/workbox-google-analytics-7.0.0.tgz#603b2c4244af1e85de0fb26287d4e17d3293452a"
|
||||
integrity sha512-MEYM1JTn/qiC3DbpvP2BVhyIH+dV/5BjHk756u9VbwuAhu0QHyKscTnisQuz21lfRpOwiS9z4XdqeVAKol0bzg==
|
||||
dependencies:
|
||||
workbox-background-sync "6.6.1"
|
||||
workbox-core "6.6.1"
|
||||
workbox-routing "6.6.1"
|
||||
workbox-strategies "6.6.1"
|
||||
workbox-background-sync "7.0.0"
|
||||
workbox-core "7.0.0"
|
||||
workbox-routing "7.0.0"
|
||||
workbox-strategies "7.0.0"
|
||||
|
||||
workbox-navigation-preload@6.6.1:
|
||||
version "6.6.1"
|
||||
resolved "https://registry.yarnpkg.com/workbox-navigation-preload/-/workbox-navigation-preload-6.6.1.tgz#61a34fe125558dd88cf09237f11bd966504ea059"
|
||||
integrity sha512-DQCZowCecO+wRoIxJI2V6bXWK6/53ff+hEXLGlQL4Rp9ZaPDLrgV/32nxwWIP7QpWDkVEtllTAK5h6cnhxNxDA==
|
||||
workbox-navigation-preload@7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/workbox-navigation-preload/-/workbox-navigation-preload-7.0.0.tgz#4913878dbbd97057181d57baa18d2bbdde085c6c"
|
||||
integrity sha512-juWCSrxo/fiMz3RsvDspeSLGmbgC0U9tKqcUPZBCf35s64wlaLXyn2KdHHXVQrb2cqF7I0Hc9siQalainmnXJA==
|
||||
dependencies:
|
||||
workbox-core "6.6.1"
|
||||
workbox-core "7.0.0"
|
||||
|
||||
workbox-precaching@6.6.1, workbox-precaching@^6.6.0:
|
||||
version "6.6.1"
|
||||
resolved "https://registry.yarnpkg.com/workbox-precaching/-/workbox-precaching-6.6.1.tgz#dedeeba10a2d163d990bf99f1c2066ac0d1a19e2"
|
||||
integrity sha512-K4znSJ7IKxCnCYEdhNkMr7X1kNh8cz+mFgx9v5jFdz1MfI84pq8C2zG+oAoeE5kFrUf7YkT5x4uLWBNg0DVZ5A==
|
||||
workbox-precaching@7.0.0, workbox-precaching@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/workbox-precaching/-/workbox-precaching-7.0.0.tgz#3979ba8033aadf3144b70e9fe631d870d5fbaa03"
|
||||
integrity sha512-EC0vol623LJqTJo1mkhD9DZmMP604vHqni3EohhQVwhJlTgyKyOkMrZNy5/QHfOby+39xqC01gv4LjOm4HSfnA==
|
||||
dependencies:
|
||||
workbox-core "6.6.1"
|
||||
workbox-routing "6.6.1"
|
||||
workbox-strategies "6.6.1"
|
||||
workbox-core "7.0.0"
|
||||
workbox-routing "7.0.0"
|
||||
workbox-strategies "7.0.0"
|
||||
|
||||
workbox-range-requests@6.6.1:
|
||||
version "6.6.1"
|
||||
resolved "https://registry.yarnpkg.com/workbox-range-requests/-/workbox-range-requests-6.6.1.tgz#ddaf7e73af11d362fbb2f136a9063a4c7f507a39"
|
||||
integrity sha512-4BDzk28govqzg2ZpX0IFkthdRmCKgAKreontYRC5YsAPB2jDtPNxqx3WtTXgHw1NZalXpcH/E4LqUa9+2xbv1g==
|
||||
workbox-range-requests@7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/workbox-range-requests/-/workbox-range-requests-7.0.0.tgz#97511901e043df27c1aa422adcc999a7751f52ed"
|
||||
integrity sha512-SxAzoVl9j/zRU9OT5+IQs7pbJBOUOlriB8Gn9YMvi38BNZRbM+RvkujHMo8FOe9IWrqqwYgDFBfv6sk76I1yaQ==
|
||||
dependencies:
|
||||
workbox-core "6.6.1"
|
||||
workbox-core "7.0.0"
|
||||
|
||||
workbox-recipes@6.6.1:
|
||||
version "6.6.1"
|
||||
resolved "https://registry.yarnpkg.com/workbox-recipes/-/workbox-recipes-6.6.1.tgz#ea70d2b2b0b0bce8de0a9d94f274d4a688e69fae"
|
||||
integrity sha512-/oy8vCSzromXokDA+X+VgpeZJvtuf8SkQ8KL0xmRivMgJZrjwM3c2tpKTJn6PZA6TsbxGs3Sc7KwMoZVamcV2g==
|
||||
workbox-recipes@7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/workbox-recipes/-/workbox-recipes-7.0.0.tgz#1a6a01c8c2dfe5a41eef0fed3fe517e8a45c6514"
|
||||
integrity sha512-DntcK9wuG3rYQOONWC0PejxYYIDHyWWZB/ueTbOUDQgefaeIj1kJ7pdP3LZV2lfrj8XXXBWt+JDRSw1lLLOnww==
|
||||
dependencies:
|
||||
workbox-cacheable-response "6.6.1"
|
||||
workbox-core "6.6.1"
|
||||
workbox-expiration "6.6.1"
|
||||
workbox-precaching "6.6.1"
|
||||
workbox-routing "6.6.1"
|
||||
workbox-strategies "6.6.1"
|
||||
workbox-cacheable-response "7.0.0"
|
||||
workbox-core "7.0.0"
|
||||
workbox-expiration "7.0.0"
|
||||
workbox-precaching "7.0.0"
|
||||
workbox-routing "7.0.0"
|
||||
workbox-strategies "7.0.0"
|
||||
|
||||
workbox-routing@6.6.1, workbox-routing@^6.6.0:
|
||||
version "6.6.1"
|
||||
resolved "https://registry.yarnpkg.com/workbox-routing/-/workbox-routing-6.6.1.tgz#cba9a1c7e0d1ea11e24b6f8c518840efdc94f581"
|
||||
integrity sha512-j4ohlQvfpVdoR8vDYxTY9rA9VvxTHogkIDwGdJ+rb2VRZQ5vt1CWwUUZBeD/WGFAni12jD1HlMXvJ8JS7aBWTg==
|
||||
workbox-routing@7.0.0, workbox-routing@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/workbox-routing/-/workbox-routing-7.0.0.tgz#6668438a06554f60645aedc77244a4fe3a91e302"
|
||||
integrity sha512-8YxLr3xvqidnbVeGyRGkaV4YdlKkn5qZ1LfEePW3dq+ydE73hUUJJuLmGEykW3fMX8x8mNdL0XrWgotcuZjIvA==
|
||||
dependencies:
|
||||
workbox-core "6.6.1"
|
||||
workbox-core "7.0.0"
|
||||
|
||||
workbox-strategies@6.6.1, workbox-strategies@^6.6.0:
|
||||
version "6.6.1"
|
||||
resolved "https://registry.yarnpkg.com/workbox-strategies/-/workbox-strategies-6.6.1.tgz#38d0f0fbdddba97bd92e0c6418d0b1a2ccd5b8bf"
|
||||
integrity sha512-WQLXkRnsk4L81fVPkkgon1rZNxnpdO5LsO+ws7tYBC6QQQFJVI6v98klrJEjFtZwzw/mB/HT5yVp7CcX0O+mrw==
|
||||
workbox-strategies@7.0.0, workbox-strategies@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/workbox-strategies/-/workbox-strategies-7.0.0.tgz#dcba32b3f3074476019049cc490fe1a60ea73382"
|
||||
integrity sha512-dg3qJU7tR/Gcd/XXOOo7x9QoCI9nk74JopaJaYAQ+ugLi57gPsXycVdBnYbayVj34m6Y8ppPwIuecrzkpBVwbA==
|
||||
dependencies:
|
||||
workbox-core "6.6.1"
|
||||
workbox-core "7.0.0"
|
||||
|
||||
workbox-streams@6.6.1:
|
||||
version "6.6.1"
|
||||
resolved "https://registry.yarnpkg.com/workbox-streams/-/workbox-streams-6.6.1.tgz#b2f7ba7b315c27a6e3a96a476593f99c5d227d26"
|
||||
integrity sha512-maKG65FUq9e4BLotSKWSTzeF0sgctQdYyTMq529piEN24Dlu9b6WhrAfRpHdCncRS89Zi2QVpW5V33NX8PgH3Q==
|
||||
workbox-streams@7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/workbox-streams/-/workbox-streams-7.0.0.tgz#36722aecd04785f88b6f709e541c094fc658c0f9"
|
||||
integrity sha512-moVsh+5to//l6IERWceYKGiftc+prNnqOp2sgALJJFbnNVpTXzKISlTIsrWY+ogMqt+x1oMazIdHj25kBSq/HQ==
|
||||
dependencies:
|
||||
workbox-core "6.6.1"
|
||||
workbox-routing "6.6.1"
|
||||
workbox-core "7.0.0"
|
||||
workbox-routing "7.0.0"
|
||||
|
||||
workbox-sw@6.6.1:
|
||||
version "6.6.1"
|
||||
resolved "https://registry.yarnpkg.com/workbox-sw/-/workbox-sw-6.6.1.tgz#d4c4ca3125088e8b9fd7a748ed537fa0247bd72c"
|
||||
integrity sha512-R7whwjvU2abHH/lR6kQTTXLHDFU2izht9kJOvBRYK65FbwutT4VvnUAJIgHvfWZ/fokrOPhfoWYoPCMpSgUKHQ==
|
||||
workbox-sw@7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/workbox-sw/-/workbox-sw-7.0.0.tgz#7350126411e3de1409f7ec243df8d06bb5b08b86"
|
||||
integrity sha512-SWfEouQfjRiZ7GNABzHUKUyj8pCoe+RwjfOIajcx6J5mtgKkN+t8UToHnpaJL5UVVOf5YhJh+OHhbVNIHe+LVA==
|
||||
|
||||
workbox-webpack-plugin@^6.6.0:
|
||||
version "6.6.1"
|
||||
resolved "https://registry.yarnpkg.com/workbox-webpack-plugin/-/workbox-webpack-plugin-6.6.1.tgz#4f81cc1ad4e5d2cd7477a86ba83c84ee2d187531"
|
||||
integrity sha512-zpZ+ExFj9NmiI66cFEApyjk7hGsfJ1YMOaLXGXBoZf0v7Iu6hL0ZBe+83mnDq3YYWAfA3fnyFejritjOHkFcrA==
|
||||
workbox-webpack-plugin@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/workbox-webpack-plugin/-/workbox-webpack-plugin-7.0.0.tgz#6c61661a2cacde1239192a5877a041a2943d1a55"
|
||||
integrity sha512-R1ZzCHPfzeJjLK2/TpKUhxSQ3fFDCxlWxgRhhSjMQLz3G2MlBnyw/XeYb34e7SGgSv0qG22zEhMIzjMNqNeKbw==
|
||||
dependencies:
|
||||
fast-json-stable-stringify "^2.1.0"
|
||||
pretty-bytes "^5.4.1"
|
||||
upath "^1.2.0"
|
||||
webpack-sources "^1.4.3"
|
||||
workbox-build "6.6.1"
|
||||
workbox-build "7.0.0"
|
||||
|
||||
workbox-window@6.6.1, workbox-window@^6.6.0:
|
||||
version "6.6.1"
|
||||
resolved "https://registry.yarnpkg.com/workbox-window/-/workbox-window-6.6.1.tgz#f22a394cbac36240d0dadcbdebc35f711bb7b89e"
|
||||
integrity sha512-wil4nwOY58nTdCvif/KEZjQ2NP8uk3gGeRNy2jPBbzypU4BT4D9L8xiwbmDBpZlSgJd2xsT9FvSNU0gsxV51JQ==
|
||||
workbox-window@7.0.0, workbox-window@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/workbox-window/-/workbox-window-7.0.0.tgz#a683ab33c896e4f16786794eac7978fc98a25d08"
|
||||
integrity sha512-j7P/bsAWE/a7sxqTzXo3P2ALb1reTfZdvVp6OJ/uLr/C2kZAMvjeWGm8V4htQhor7DOvYg0sSbFN2+flT5U0qA==
|
||||
dependencies:
|
||||
"@types/trusted-types" "^2.0.2"
|
||||
workbox-core "6.6.1"
|
||||
workbox-core "7.0.0"
|
||||
|
||||
wrap-ansi@^5.1.0:
|
||||
version "5.1.0"
|
||||
|
@ -12485,9 +12502,9 @@ write-file-atomic@^5.0.1:
|
|||
signal-exit "^4.0.1"
|
||||
|
||||
ws@^6.2.1:
|
||||
version "6.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"
|
||||
integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==
|
||||
version "6.2.2"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e"
|
||||
integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==
|
||||
dependencies:
|
||||
async-limiter "~1.0.0"
|
||||
|
||||
|
|
Loading…
Reference in New Issue