Merge commit '54ba842786' into kb_migration

This commit is contained in:
KMY 2023-08-07 22:07:56 +09:00
commit ad487e4545
60 changed files with 546 additions and 136 deletions

View file

@ -38,6 +38,7 @@ linters:
- 'app/views/invites/_form.html.haml' - 'app/views/invites/_form.html.haml'
- 'app/views/relationships/_account.html.haml' - 'app/views/relationships/_account.html.haml'
- 'app/views/shared/_og.html.haml' - 'app/views/shared/_og.html.haml'
- 'app/views/application/_sidebar.html.haml'
# Offense count: 3 # Offense count: 3
IdNames: IdNames:

View file

@ -7,7 +7,7 @@ kmyblue はフォーク名であり、同時に[サーバー名](https://kmy.blu
kmyblue は AGPL ライセンスで公開されているため、どなたでも自由にフォークし、このソースコードを元に自分でサーバーを立てて公開することができます。また ActivityPub に参加することもできます。サーバーkmyblueは創作作家向けのものですが、フォークとしてのkmyblueは作者の嫌いな政治に関する過激な話を取り扱うコミュニティ、創作活動の一部エロ関係含むまたは全体を否定するコミュニティなども平等にお使いいただけますし、サーバーkmyblueのルールを適用する必要もありません。 kmyblue は AGPL ライセンスで公開されているため、どなたでも自由にフォークし、このソースコードを元に自分でサーバーを立てて公開することができます。また ActivityPub に参加することもできます。サーバーkmyblueは創作作家向けのものですが、フォークとしてのkmyblueは作者の嫌いな政治に関する過激な話を取り扱うコミュニティ、創作活動の一部エロ関係含むまたは全体を否定するコミュニティなども平等にお使いいただけますし、サーバーkmyblueのルールを適用する必要もありません。
ただし kmyblue においてテストコードは飾りでしかないため、不具合が発生しても自己責任になります。既知のバグもいくつかありますし、直す予定のないものも含まれます。 ただし kmyblue においてテストコードは飾りでしかないため、不具合が発生しても自己責任になります。既知のバグもいくつかありますし、直す予定のないものも含まれます。
テストコードは飾りですが、Lint は動いています。 テストコード、Lint どちらも動いています。
## kmyblue の強み ## kmyblue の強み

View file

@ -88,15 +88,18 @@ module Admin
end end
def update_params def update_params
params.require(:domain_block).permit(:severity, :reject_media, :reject_favourite, :reject_reply, :reject_reply_exclude_followers, :reject_send_not_public_searchability, :reject_send_public_unlisted, :reject_send_dissubscribable, :reject_send_media, :reject_send_sensitive, :reject_hashtag, :reject_straight_follow, :reject_new_follow, :detect_invalid_subscription, :reject_reports, :private_comment, :public_comment, :obfuscate, :hidden, :hidden_anonymous) params.require(:domain_block).permit(:severity, :reject_media, :reject_favourite, :reject_reply, :reject_reply_exclude_followers, :reject_send_not_public_searchability, :reject_send_public_unlisted, :reject_send_dissubscribable, :reject_send_media, :reject_send_sensitive, :reject_hashtag,
:reject_straight_follow, :reject_new_follow, :detect_invalid_subscription, :reject_reports, :private_comment, :public_comment, :obfuscate, :hidden, :hidden_anonymous)
end end
def resource_params def resource_params
params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_favourite, :reject_reply, :reject_reply_exclude_followers, :reject_send_not_public_searchability, :reject_send_public_unlisted, :reject_send_dissubscribable, :reject_send_media, :reject_send_sensitive, :reject_hashtag, :reject_straight_follow, :reject_new_follow, :detect_invalid_subscription, :reject_reports, :private_comment, :public_comment, :obfuscate, :hidden, :hidden_anonymous) params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_favourite, :reject_reply, :reject_reply_exclude_followers, :reject_send_not_public_searchability, :reject_send_public_unlisted, :reject_send_dissubscribable, :reject_send_media, :reject_send_sensitive, :reject_hashtag,
:reject_straight_follow, :reject_new_follow, :detect_invalid_subscription, :reject_reports, :private_comment, :public_comment, :obfuscate, :hidden, :hidden_anonymous)
end end
def form_domain_block_batch_params def form_domain_block_batch_params
params.require(:form_domain_block_batch).permit(domain_blocks_attributes: [:enabled, :domain, :severity, :reject_media, :reject_favourite, :reject_reply, :reject_reply_exclude_followers, :reject_send_not_public_searchability, :reject_send_public_unlisted, :reject_send_dissubscribable, :reject_send_media, :reject_send_sensitive, :reject_hashtag, :reject_straight_follow, :reject_new_follow, :detect_invalid_subscription, :reject_reports, :private_comment, :public_comment, :obfuscate, :hidden, :hidden_anonymous]) params.require(:form_domain_block_batch).permit(domain_blocks_attributes: [:enabled, :domain, :severity, :reject_media, :reject_favourite, :reject_reply, :reject_reply_exclude_followers, :reject_send_not_public_searchability, :reject_send_public_unlisted, :reject_send_dissubscribable, :reject_send_media,
:reject_send_sensitive, :reject_hashtag, :reject_straight_follow, :reject_new_follow, :detect_invalid_subscription, :reject_reports, :private_comment, :public_comment, :obfuscate, :hidden, :hidden_anonymous])
end end
def action_from_button def action_from_button

View file

@ -69,7 +69,8 @@ class Api::V1::Admin::DomainBlocksController < Api::BaseController
end end
def domain_block_params def domain_block_params
params.permit(:severity, :reject_media, :reject_favourite, :reject_reply, :reject_reply_exclude_followers, :reject_reports, :reject_send_not_public_searchability, :reject_send_public_unlisted, :reject_send_dissubscribable, :reject_send_media, :reject_send_sensitive, :reject_hashtag, :reject_straight_follow, :reject_new_follow, :detect_invalid_subscription, :private_comment, :public_comment, :obfuscate, :hidden, :hidden_anonymous) params.permit(:severity, :reject_media, :reject_favourite, :reject_reply, :reject_reply_exclude_followers, :reject_reports, :reject_send_not_public_searchability, :reject_send_public_unlisted, :reject_send_dissubscribable, :reject_send_media, :reject_send_sensitive, :reject_hashtag, :reject_straight_follow,
:reject_new_follow, :detect_invalid_subscription, :private_comment, :public_comment, :obfuscate, :hidden, :hidden_anonymous)
end end
def insert_pagination_headers def insert_pagination_headers
@ -101,6 +102,7 @@ class Api::V1::Admin::DomainBlocksController < Api::BaseController
end end
def resource_params def resource_params
params.permit(:domain, :severity, :reject_media, :reject_favourite, :reject_reply, :reject_reply_exclude_followers, :reject_send_not_public_searchability, :reject_send_public_unlisted, :reject_send_dissubscribable, :reject_send_media, :reject_send_sensitive, :reject_hashtag, :reject_straight_follow, :reject_new_follow, :detect_invalid_subscription, :reject_reports, :private_comment, :public_comment, :obfuscate, :hidden, :hidden_anonymous) params.permit(:domain, :severity, :reject_media, :reject_favourite, :reject_reply, :reject_reply_exclude_followers, :reject_send_not_public_searchability, :reject_send_public_unlisted, :reject_send_dissubscribable, :reject_send_media, :reject_send_sensitive, :reject_hashtag, :reject_straight_follow,
:reject_new_follow, :detect_invalid_subscription, :reject_reports, :private_comment, :public_comment, :obfuscate, :hidden, :hidden_anonymous)
end end
end end

View file

@ -1,4 +1,5 @@
import api from '../api'; import api from '../api';
import { importFetchedAccounts } from './importer'; import { importFetchedAccounts } from './importer';
export const ANTENNAS_FETCH_REQUEST = 'ANTENNAS_FETCH_REQUEST'; export const ANTENNAS_FETCH_REQUEST = 'ANTENNAS_FETCH_REQUEST';

View file

@ -1,4 +1,5 @@
import api, { getLinks } from '../api'; import api, { getLinks } from '../api';
import { importFetchedStatuses } from './importer'; import { importFetchedStatuses } from './importer';
export const EMOJI_REACTED_STATUSES_FETCH_REQUEST = 'EMOJI_REACTED_STATUSES_FETCH_REQUEST'; export const EMOJI_REACTED_STATUSES_FETCH_REQUEST = 'EMOJI_REACTED_STATUSES_FETCH_REQUEST';

View file

@ -1,5 +1,6 @@
import { PureComponent } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import emojify from '../features/emoji/emoji'; import emojify from '../features/emoji/emoji';
export default class EmojiView extends PureComponent { export default class EmojiView extends PureComponent {

View file

@ -33,7 +33,11 @@ export const ShortNumberRenderer: React.FC<ShortNumberProps> = ({
const customRenderer = children ?? renderer ?? null; const customRenderer = children ?? renderer ?? null;
const displayNumber = !isHide ? <ShortNumberCounter value={shortNumber} /> : <span>-</span>; const displayNumber = !isHide ? (
<ShortNumberCounter value={shortNumber} />
) : (
<span>-</span>
);
return ( return (
customRenderer?.(displayNumber, pluralReady(value, division)) ?? customRenderer?.(displayNumber, pluralReady(value, division)) ??

View file

@ -1,8 +1,12 @@
import { PureComponent } from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import { injectIntl } from 'react-intl'; import { injectIntl } from 'react-intl';
import classNames from 'classnames'; import classNames from 'classnames';
import ImmutablePropTypes from 'react-immutable-proptypes';
import EmojiView from './emoji_view'; import EmojiView from './emoji_view';
class EmojiReactionButton extends PureComponent { class EmojiReactionButton extends PureComponent {

View file

@ -1,10 +1,12 @@
import { connect } from 'react-redux'; import { injectIntl } from 'react-intl';
import { makeGetAccount } from '../../../selectors';
import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import { Avatar } from '../../../components/avatar'; import { Avatar } from '../../../components/avatar';
import { DisplayName } from '../../../components/display_name'; import { DisplayName } from '../../../components/display_name';
import { injectIntl } from 'react-intl'; import { makeGetAccount } from '../../../selectors';
const makeMapStateToProps = () => { const makeMapStateToProps = () => {
const getAccount = makeGetAccount(); const getAccount = makeGetAccount();

View file

@ -1,12 +1,16 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { IconButton } from '../../../components/icon_button';
import { defineMessages, injectIntl } from 'react-intl'; import { defineMessages, injectIntl } from 'react-intl';
import { removeFromAntennaAdder, addToAntennaAdder } from '../../../actions/antennas';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import { removeFromAntennaAdder, addToAntennaAdder } from '../../../actions/antennas';
import { IconButton } from '../../../components/icon_button';
const messages = defineMessages({ const messages = defineMessages({
remove: { id: 'lists.account.remove', defaultMessage: 'Remove from list' }, remove: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
add: { id: 'lists.account.add', defaultMessage: 'Add to list' }, add: { id: 'lists.account.add', defaultMessage: 'Add to list' },

View file

@ -1,12 +1,16 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { injectIntl } from 'react-intl'; import { injectIntl } from 'react-intl';
import { setupAntennaAdder, resetAntennaAdder } from '../../actions/antennas';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import Antenna from './components/antenna';
import { setupAntennaAdder, resetAntennaAdder } from '../../actions/antennas';
import Account from './components/account'; import Account from './components/account';
import Antenna from './components/antenna';
// hack // hack
const getOrderedAntennas = createSelector([state => state.get('antennas')], antennas => { const getOrderedAntennas = createSelector([state => state.get('antennas')], antennas => {

View file

@ -1,8 +1,10 @@
import { connect } from 'react-redux';
import TextIconButton from '../components/text_icon_button';
import { changeComposeMarkdown } from '../../../actions/compose';
import { injectIntl, defineMessages } from 'react-intl'; import { injectIntl, defineMessages } from 'react-intl';
import { connect } from 'react-redux';
import { changeComposeMarkdown } from '../../../actions/compose';
import TextIconButton from '../components/text_icon_button';
const messages = defineMessages({ const messages = defineMessages({
marked: { id: 'compose_form.markdown.marked', defaultMessage: 'Markdown is enabled' }, marked: { id: 'compose_form.markdown.marked', defaultMessage: 'Markdown is enabled' },
unmarked: { id: 'compose_form.markdown.unmarked', defaultMessage: 'Markdown is disabled' }, unmarked: { id: 'compose_form.markdown.unmarked', defaultMessage: 'Markdown is disabled' },

View file

@ -1,10 +1,15 @@
import { debounce } from 'lodash';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { Helmet } from 'react-helmet'; import { Helmet } from 'react-helmet';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { debounce } from 'lodash';
import { addColumn, removeColumn, moveColumn } from 'mastodon/actions/columns'; import { addColumn, removeColumn, moveColumn } from 'mastodon/actions/columns';
import { fetchEmojiReactedStatuses, expandEmojiReactedStatuses } from 'mastodon/actions/emoji_reactions'; import { fetchEmojiReactedStatuses, expandEmojiReactedStatuses } from 'mastodon/actions/emoji_reactions';
import ColumnHeader from 'mastodon/components/column_header'; import ColumnHeader from 'mastodon/components/column_header';

View file

@ -446,7 +446,6 @@ class Notification extends ImmutablePureComponent {
renderWarning (notification) { renderWarning (notification) {
const { intl, unread } = this.props; const { intl, unread } = this.props;
console.dir(notification);
return ( return (
<HotKeys handlers={this.getHandlers()}> <HotKeys handlers={this.getHandlers()}>

View file

@ -1,4 +1,5 @@
import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
import { import {
ANTENNA_ADDER_RESET, ANTENNA_ADDER_RESET,
ANTENNA_ADDER_SETUP, ANTENNA_ADDER_SETUP,

View file

@ -1,4 +1,5 @@
import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
import { import {
ANTENNA_ACCOUNTS_FETCH_REQUEST, ANTENNA_ACCOUNTS_FETCH_REQUEST,
ANTENNA_ACCOUNTS_FETCH_SUCCESS, ANTENNA_ACCOUNTS_FETCH_SUCCESS,

View file

@ -1,7 +1,8 @@
import { Map as ImmutableMap, fromJS } from 'immutable';
import { import {
ANTENNAS_FETCH_SUCCESS, ANTENNAS_FETCH_SUCCESS,
} from '../actions/antennas'; } from '../actions/antennas';
import { Map as ImmutableMap, fromJS } from 'immutable';
const initialState = ImmutableMap(); const initialState = ImmutableMap();

View file

@ -507,7 +507,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
SCAN_SEARCHABILITY_FEDIBIRD_RE = /searchable_by_(all_users|followers_only|reacted_users_only|nobody)/ SCAN_SEARCHABILITY_FEDIBIRD_RE = /searchable_by_(all_users|followers_only|reacted_users_only|nobody)/
def searchability def searchability
searchability_from_audience || searchability_from_bio || (marked_as_misskey_searchability? ? :public : nil) searchability_from_audience || searchability_from_bio || (misskey_software? ? misskey_searchability : nil)
end end
def searchability_from_bio def searchability_from_bio
@ -522,14 +522,26 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
searchability = :public if %w(public all_users).include?(searchability) searchability = :public if %w(public all_users).include?(searchability)
searchability = :private if %w(followers followers_only).include?(searchability) searchability = :private if %w(followers followers_only).include?(searchability)
searchability = :limited if %w(private reacted_users_only).include?(searchability) searchability = :direct if %w(reactors reacted_users_only).include?(searchability)
searchability = :direct if %w(reactors nobody).include?(searchability) searchability = :limited if %w(private nobody).include?(searchability)
searchability searchability
end end
def marked_as_misskey_searchability? def instance_info
@marked_as_misskey_searchability ||= DomainBlock.detect_invalid_subscription?(@account.domain) @instance_info ||= InstanceInfo.find_by(domain: @account.domain)
end
def misskey_software?
info = instance_info
return false if info.nil?
%w(misskey calckey firefish).include?(info.software)
end
def misskey_searchability
visibility = visibility_from_audience
%i(public unlisted).include?(visibility) ? :public : :limited
end end
def visibility_from_audience def visibility_from_audience

View file

@ -153,9 +153,10 @@ class StatusReachFinder
end end
def banned_domains_for_misskey_of_status(status) def banned_domains_for_misskey_of_status(status)
blocks = DomainBlock.where(domain: nil) return [] unless (status.public_unlisted_visibility? && status.account.user&.setting_reject_public_unlisted_subscription) || (status.unlisted_visibility? && status.account.user&.setting_reject_unlisted_subscription)
blocks = blocks.or(DomainBlock.where(detect_invalid_subscription: true)) if status.public_unlisted_visibility? && status.account.user&.setting_reject_public_unlisted_subscription
blocks = blocks.or(DomainBlock.where(detect_invalid_subscription: true)) if status.unlisted_visibility? && status.account.user&.setting_reject_unlisted_subscription from_info = InstanceInfo.where(software: %w(misskey calckey firefish)).pluck(:domain)
blocks.pluck(:domain).uniq from_domain_block = DomainBlock.where(detect_invalid_subscription: true).pluck(:domain)
(from_info + from_domain_block).uniq
end end
end end

View file

@ -40,7 +40,7 @@ class CustomEmoji < ApplicationRecord
belongs_to :category, class_name: 'CustomEmojiCategory', optional: true belongs_to :category, class_name: 'CustomEmojiCategory', optional: true
has_one :local_counterpart, -> { where(domain: nil) }, class_name: 'CustomEmoji', primary_key: :shortcode, foreign_key: :shortcode, inverse_of: false has_one :local_counterpart, -> { where(domain: nil) }, class_name: 'CustomEmoji', primary_key: :shortcode, foreign_key: :shortcode, inverse_of: false
has_many :emoji_reactions, inverse_of: :custom_emoji, dependent: :destroy has_many :emoji_reactions, inverse_of: :custom_emoji
has_attached_file :image, styles: { static: { format: 'png', convert_options: '-coalesce +profile "!icc,*" +set modify-date +set create-date' } }, validate_media_type: false has_attached_file :image, styles: { static: { format: 'png', convert_options: '-coalesce +profile "!icc,*" +set modify-date +set create-date' } }, validate_media_type: false

View file

@ -19,6 +19,7 @@ class Instance < ApplicationRecord
belongs_to :domain_block belongs_to :domain_block
belongs_to :domain_allow belongs_to :domain_allow
belongs_to :unavailable_domain # skipcq: RB-RL1031 belongs_to :unavailable_domain # skipcq: RB-RL1031
belongs_to :instance_info
end end
scope :searchable, -> { where.not(domain: DomainBlock.select(:domain)) } scope :searchable, -> { where.not(domain: DomainBlock.select(:domain)) }

View file

@ -0,0 +1,17 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: instance_infos
#
# id :bigint(8) not null, primary key
# domain :string default(""), not null
# software :string default(""), not null
# version :string default(""), not null
# data :jsonb not null
# created_at :datetime not null
# updated_at :datetime not null
#
class InstanceInfo < ApplicationRecord
end

View file

@ -60,6 +60,7 @@ class UserSettings
setting :pending_account, default: true setting :pending_account, default: true
setting :trends, default: true setting :trends, default: true
setting :appeal, default: true setting :appeal, default: true
setting :warning, default: true
end end
namespace :interactions do namespace :interactions do

View file

@ -66,8 +66,12 @@ class REST::StatusSerializer < ActiveModel::Serializer
end end
def visibility_ex def visibility_ex
if object.limited_visibility?
'private'
else
object.visibility object.visibility
end end
end
def searchability def searchability
object.compute_searchability object.compute_searchability

View file

@ -46,6 +46,7 @@ class ActivityPub::ProcessAccountService < BaseService
end end
create_account create_account
fetch_instance_info
end end
update_account update_account
@ -81,7 +82,7 @@ class ActivityPub::ProcessAccountService < BaseService
@account.suspended_at = domain_block.created_at if auto_suspend? @account.suspended_at = domain_block.created_at if auto_suspend?
@account.suspension_origin = :local if auto_suspend? @account.suspension_origin = :local if auto_suspend?
@account.silenced_at = domain_block.created_at if auto_silence? @account.silenced_at = domain_block.created_at if auto_silence?
@account.searchability = :private # not null @account.searchability = :direct # not null
@account.dissubscribable = false # not null @account.dissubscribable = false # not null
set_immediate_protocol_attributes! set_immediate_protocol_attributes!
@ -207,6 +208,10 @@ class ActivityPub::ProcessAccountService < BaseService
AccountMergingWorker.perform_async(@account.id) AccountMergingWorker.perform_async(@account.id)
end end
def fetch_instance_info
FetchInstanceInfoWorker.perform_async(@account.domain) unless InstanceInfo.exists?(domain: @account.domain)
end
def actor_type def actor_type
if @json['type'].is_a?(Array) if @json['type'].is_a?(Array)
@json['type'].find { |type| ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES.include?(type) } @json['type'].find { |type| ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES.include?(type) }
@ -258,7 +263,7 @@ class ActivityPub::ProcessAccountService < BaseService
bio = searchability_from_bio bio = searchability_from_bio
return bio unless bio.nil? return bio unless bio.nil?
return marked_as_misskey_searchability? ? :public : :private return misskey_software? ? :public : :direct
end end
if audience_searchable_by.any? { |uri| ActivityPub::TagManager.instance.public_collection?(uri) } if audience_searchable_by.any? { |uri| ActivityPub::TagManager.instance.public_collection?(uri) }
@ -282,14 +287,21 @@ class ActivityPub::ProcessAccountService < BaseService
searchability = :public if %w(public all_users).include?(searchability) searchability = :public if %w(public all_users).include?(searchability)
searchability = :private if %w(followers followers_only).include?(searchability) searchability = :private if %w(followers followers_only).include?(searchability)
searchability = :limited if %w(private reacted_users_only).include?(searchability) searchability = :direct if %w(reactors reacted_users_only).include?(searchability)
searchability = :direct if %w(reactors nobody).include?(searchability) searchability = :limited if %w(private nobody).include?(searchability)
searchability searchability
end end
def marked_as_misskey_searchability? def instance_info
domain_block&.detect_invalid_subscription @instance_info ||= InstanceInfo.find_by(domain: @domain)
end
def misskey_software?
info = instance_info
return false if info.nil?
%w(misskey calckey firefish).include?(info.software)
end end
def subscribable_by def subscribable_by

View file

@ -45,7 +45,7 @@ class SearchService < BaseService
end end
def perform_statuses_search! def perform_statuses_search!
privacy_definition = parsed_query.apply(StatusesIndex.filter(term: { searchable_by: @account.id }).track_scores(true).min_score(@min_score)) privacy_definition = parsed_query.apply(StatusesIndex.filter(terms: { searchability: %w(public private direct) }).filter(term: { searchable_by: @account.id }).track_scores(true).min_score(@min_score))
# 'direct' searchability posts are NOT in here because it's already added at previous line. # 'direct' searchability posts are NOT in here because it's already added at previous line.
case @searchability case @searchability

View file

@ -43,6 +43,7 @@ class UpdateStatusService < BaseService
update_metadata! update_metadata!
broadcast_updates! broadcast_updates!
@status.reload
@status @status
rescue NoChangesSubmittedError rescue NoChangesSubmittedError
# For calls that result in no changes, swallow the error # For calls that result in no changes, swallow the error

View file

@ -1,6 +1,10 @@
- content_for :page_title do - content_for :page_title do
= @instance.domain = @instance.domain
-if @instance.instance_info.present?
%p
= "#{@instance.instance_info.software} #{@instance.instance_info.version}"
- content_for :header_tags do - content_for :header_tags do
= javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous' = javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous'

View file

@ -4,7 +4,7 @@
%tbody %tbody
%tr %tr
%td %td
%img{ src: media_attachment.url, class: 'image' } %img.image{ src: media_attachment.url, alt: '' }
.detailed-status__meta .detailed-status__meta
- if media_attachment.status.application - if media_attachment.status.application
= media_attachment.status.application.name = media_attachment.status.application.name

View file

@ -45,7 +45,7 @@
%th= t('admin.statuses.favourites') %th= t('admin.statuses.favourites')
%td= friendly_number_to_human @status.favourites_count %td= friendly_number_to_human @status.favourites_count
%div.action-buttons .action-buttons
%div %div
- if @account.local? - if @account.local?
= link_to t('admin.statuses.remove_history'), remove_history_admin_account_status_path(@account.id), data: { confirm: t('admin.statuses.are_you_sure') }, method: :post, class: 'button' if can?(:warn, @account) = link_to t('admin.statuses.remove_history'), remove_history_admin_account_status_path(@account.id), data: { confirm: t('admin.statuses.are_you_sure') }, method: :post, class: 'button' if can?(:warn, @account)

View file

@ -38,7 +38,7 @@
= yield :header_tags = yield :header_tags
%script(src="https://www.googletagmanager.com/gtag/js?id=AW-11130587137" async) %script{ src="https://www.googletagmanager.com/gtag/js?id=AW-11130587137" async }
:javascript :javascript
window.dataLayer = window.dataLayer || []; window.dataLayer = window.dataLayer || [];

View file

@ -78,7 +78,7 @@
.fields-group .fields-group
= f.input :my_actor_type, collection: ['person', 'bot'],label_method: lambda { |item| safe_join([t("simple_form.labels.defaults.#{item}"), content_tag(:span, I18n.t("simple_form.hints.defaults.#{item}"), class: 'hint')]) }, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', wrapper: :with_floating_label = f.input :my_actor_type, collection: ['person', 'bot'],label_method: lambda { |item| safe_join([t("simple_form.labels.defaults.#{item}"), content_tag(:span, I18n.t("simple_form.hints.defaults.#{item}"), class: 'hint')]) }, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', wrapper: :with_floating_label
-#.fields-group .fields-group
= f.input :group_allow_private_message, as: :boolean, wrapper: :with_label, kmyblue: true, hint: t('simple_form.hints.defaults.group_allow_private_message') = f.input :group_allow_private_message, as: :boolean, wrapper: :with_label, kmyblue: true, hint: t('simple_form.hints.defaults.group_allow_private_message')
.actions .actions

View file

@ -0,0 +1,77 @@
# frozen_string_literal: true
class FetchInstanceInfoWorker
include Sidekiq::Worker
include JsonLdHelper
include Redisable
include Lockable
class Error < StandardError; end
class GoneError < Error; end
class RequestError < Error; end
SUPPORTED_NOTEINFO_RELS = ['http://nodeinfo.diaspora.software/ns/schema/2.0', 'http://nodeinfo.diaspora.software/ns/schema/2.1'].freeze
def perform(domain)
@instance = Instance.find_by(domain: domain)
return if !@instance || @instance.unavailable_domain.present?
with_redis_lock("instance_info:#{domain}") do
link = nodeinfo_link
return if link.nil?
update_info!(link)
end
end
private
def nodeinfo_link
nodeinfo = fetch_json("https://#{@instance.domain}/.well-known/nodeinfo")
return nil if nodeinfo.nil? || !nodeinfo.key?('links')
nodeinfo_links = nodeinfo['links']
return nil if !nodeinfo_links.is_a?(Array) || nodeinfo_links.blank?
nodeinfo_link = nodeinfo_links.find { |item| item.key?('rel') && item.key?('href') && SUPPORTED_NOTEINFO_RELS.include?(item['rel']) }
return nil if nodeinfo_link.nil? || nodeinfo_link['href'].nil? || !nodeinfo_link['href'].start_with?('http')
nodeinfo_link['href']
end
def update_info!(url)
content = fetch_json(url)
return nil if content.nil? || !content.key?('software') || !content['software'].key?('name')
software = content['software']['name']
version = content['software'].key?('version') ? content['software']['version'] : ''
exists = @instance.instance_info
if exists.nil?
InstanceInfo.create!(domain: @instance.domain, software: software, version: version, data: content)
else
exists.software = software
exists.version = version
exists.data = content
exists.save!
end
end
def fetch_json(url)
build_request(url).perform do |response|
if [200, 203].include?(response.code)
raise Mastodon::UnexpectedResponseError, response unless response_successful?(response) || response_error_unsalvageable?(response)
body_to_json(response.body_with_limit)
elsif response.code == 410
raise FetchInstanceInfoWorker::GoneError, "#{domain} is gone from the server"
else
raise FetchInstanceInfoWorker::RequestError, "Request for #{domain} returned HTTP #{response.code}"
end
end
end
def build_request(url)
Request.new(:get, url).add_headers('Accept' => 'application/jrd+json, application/json')
end
end

View file

@ -0,0 +1,15 @@
# frozen_string_literal: true
class Scheduler::UpdateInstanceInfoScheduler
include Sidekiq::Worker
sidekiq_options retry: 1
def perform
Instance.select(:domain).reorder(nil).find_in_batches do |instances|
FetchInstanceInfoWorker.push_bulk(instances) do |instance|
[instance.domain]
end
end
end
end

View file

@ -57,6 +57,52 @@
], ],
"note": "" "note": ""
}, },
{
"warning_type": "Mass Assignment",
"warning_code": 105,
"fingerprint": "874be88fedf4c680926845e9a588d3197765a6ccbfdd76466b44cc00151c612e",
"check_name": "PermitAttributes",
"message": "Potentially dangerous key allowed for mass assignment",
"file": "app/controllers/api/v1/admin/reports_controller.rb",
"line": 88,
"link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
"code": "params.permit(:resolved, :account_id, :target_account_id)",
"render_path": null,
"location": {
"type": "method",
"class": "Api::V1::Admin::ReportsController",
"method": "filter_params"
},
"user_input": ":account_id",
"confidence": "High",
"cwe_id": [
915
],
"note": ""
},
{
"warning_type": "Mass Assignment",
"warning_code": 105,
"fingerprint": "ab5035dd1a9f8c3a8d92fb2c37e8fe86fede4f87c91b71aa32e89c9eede602fc",
"check_name": "PermitAttributes",
"message": "Potentially dangerous key allowed for mass assignment",
"file": "app/controllers/api/v1/notifications_controller.rb",
"line": 81,
"link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
"code": "params.permit(:account_id, :types => ([]), :exclude_types => ([]))",
"render_path": null,
"location": {
"type": "method",
"class": "Api::V1::NotificationsController",
"method": "browserable_params"
},
"user_input": ":account_id",
"confidence": "High",
"cwe_id": [
915
],
"note": ""
},
{ {
"warning_type": "Cross-Site Scripting", "warning_type": "Cross-Site Scripting",
"warning_code": 4, "warning_code": 4,
@ -89,8 +135,54 @@
79 79
], ],
"note": "" "note": ""
},
{
"warning_type": "Mass Assignment",
"warning_code": 105,
"fingerprint": "d0511f0287aea4ed9511f5a744f880cb15af77a8ec88f81b7365b00b642cf427",
"check_name": "PermitAttributes",
"message": "Potentially dangerous key allowed for mass assignment",
"file": "app/controllers/api/v1/reports_controller.rb",
"line": 26,
"link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
"code": "params.permit(:account_id, :comment, :category, :forward, :forward_to_domains => ([]), :status_ids => ([]), :rule_ids => ([]))",
"render_path": null,
"location": {
"type": "method",
"class": "Api::V1::ReportsController",
"method": "report_params"
},
"user_input": ":account_id",
"confidence": "High",
"cwe_id": [
915
],
"note": ""
},
{
"warning_type": "Mass Assignment",
"warning_code": 105,
"fingerprint": "dd59382eb5fda8da4d29f5d52dc8261ed9070d3c61cecc12b8332e18448b1c11",
"check_name": "PermitAttributes",
"message": "Potentially dangerous key allowed for mass assignment",
"file": "app/controllers/api/v2/search_controller.rb",
"line": 42,
"link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
"code": "params.permit(:type, :offset, :min_id, :max_id, :account_id, :following, :searchability)",
"render_path": null,
"location": {
"type": "method",
"class": "Api::V2::SearchController",
"method": "search_params"
},
"user_input": ":account_id",
"confidence": "High",
"cwe_id": [
915
],
"note": ""
} }
], ],
"updated": "2023-07-12 11:20:51 -0400", "updated": "2023-07-30 22:53:30 +0900",
"brakeman_version": "6.0.0" "brakeman_version": "6.0.1"
} }

View file

@ -62,3 +62,7 @@
interval: 30 seconds interval: 30 seconds
class: Scheduler::SidekiqHealthScheduler class: Scheduler::SidekiqHealthScheduler
queue: scheduler queue: scheduler
update_instance_info_scheduler:
cron: '0 0 * * *'
class: Scheduler::UpdateInstanceInfoScheduler
queue: scheduler

View file

@ -2,16 +2,20 @@
class AddImageSizeToCustomEmojis < ActiveRecord::Migration[6.1] class AddImageSizeToCustomEmojis < ActiveRecord::Migration[6.1]
def change def change
safety_assured do
change_table :custom_emojis, bulk: true do |t| change_table :custom_emojis, bulk: true do |t|
t.integer :image_width t.integer :image_width
t.integer :image_height t.integer :image_height
end end
end end
end
def down def down
safety_assured do
change_table :custom_emojis, bulk: true do |t| change_table :custom_emojis, bulk: true do |t|
t.remove :image_width t.remove :image_width
t.remove :image_height t.remove :image_height
end end
end end
end end
end

View file

@ -2,6 +2,8 @@
class AddSearchabilityToAccounts < ActiveRecord::Migration[6.1] class AddSearchabilityToAccounts < ActiveRecord::Migration[6.1]
def change def change
safety_assured do
add_column :accounts, :searchability, :integer, null: false, default: 0 add_column :accounts, :searchability, :integer, null: false, default: 0
end end
end end
end

View file

@ -2,6 +2,8 @@
class AddEmojiReactionsCountToStatusStats < ActiveRecord::Migration[6.1] class AddEmojiReactionsCountToStatusStats < ActiveRecord::Migration[6.1]
def change def change
safety_assured do
add_column :status_stats, :emoji_reactions_count, :integer, null: false, default: 0 add_column :status_stats, :emoji_reactions_count, :integer, null: false, default: 0
end end
end end
end

View file

@ -2,6 +2,8 @@
class AddMarkdownToStatuses < ActiveRecord::Migration[6.1] class AddMarkdownToStatuses < ActiveRecord::Migration[6.1]
def change def change
safety_assured do
add_column :statuses, :markdown, :boolean, default: false add_column :statuses, :markdown, :boolean, default: false
end end
end end
end

View file

@ -2,6 +2,8 @@
class AddMarkdownToStatusEdits < ActiveRecord::Migration[6.1] class AddMarkdownToStatusEdits < ActiveRecord::Migration[6.1]
def change def change
safety_assured do
add_column :status_edits, :markdown, :boolean, default: false add_column :status_edits, :markdown, :boolean, default: false
end end
end end
end

View file

@ -2,6 +2,8 @@
class AddEmojiReactionsCountPerAccountToStatusStats < ActiveRecord::Migration[6.1] class AddEmojiReactionsCountPerAccountToStatusStats < ActiveRecord::Migration[6.1]
def change def change
safety_assured do
add_column :status_stats, :emoji_reaction_accounts_count, :integer, null: false, default: 0 add_column :status_stats, :emoji_reaction_accounts_count, :integer, null: false, default: 0
end end
end end
end

View file

@ -2,14 +2,17 @@
class AddExcludesToAntennas < ActiveRecord::Migration[6.1] class AddExcludesToAntennas < ActiveRecord::Migration[6.1]
def change def change
safety_assured do
change_table :antennas, bulk: true do |t| change_table :antennas, bulk: true do |t|
t.jsonb :exclude_domains t.jsonb :exclude_domains
t.jsonb :exclude_accounts t.jsonb :exclude_accounts
t.jsonb :exclude_tags t.jsonb :exclude_tags
end end
end end
end
def down def down
safety_assured do
change_table :antennas, bulk: true do |t| change_table :antennas, bulk: true do |t|
t.remove :exclude_domains t.remove :exclude_domains
t.remove :exclude_accounts t.remove :exclude_accounts
@ -17,3 +20,4 @@ class AddExcludesToAntennas < ActiveRecord::Migration[6.1]
end end
end end
end end
end

View file

@ -2,6 +2,7 @@
class AddRejectSendingToDomainBlocks < ActiveRecord::Migration[6.1] class AddRejectSendingToDomainBlocks < ActiveRecord::Migration[6.1]
def change def change
safety_assured do
change_table :domain_blocks, bulk: true do |t| change_table :domain_blocks, bulk: true do |t|
t.boolean :reject_send_not_public_searchability, null: false, default: false t.boolean :reject_send_not_public_searchability, null: false, default: false
t.boolean :reject_send_unlisted_dissubscribable, null: false, default: false t.boolean :reject_send_unlisted_dissubscribable, null: false, default: false
@ -11,8 +12,10 @@ class AddRejectSendingToDomainBlocks < ActiveRecord::Migration[6.1]
t.boolean :reject_send_sensitive, null: false, default: false t.boolean :reject_send_sensitive, null: false, default: false
end end
end end
end
def down def down
safety_assured do
change_table :domain_blocks, bulk: true do |t| change_table :domain_blocks, bulk: true do |t|
t.remove :reject_send_not_public_searchability t.remove :reject_send_not_public_searchability
t.remove :reject_send_unlisted_dissubscribable t.remove :reject_send_unlisted_dissubscribable
@ -23,3 +26,4 @@ class AddRejectSendingToDomainBlocks < ActiveRecord::Migration[6.1]
end end
end end
end end
end

View file

@ -2,14 +2,17 @@
class AddSomeToDomainBlocks < ActiveRecord::Migration[6.1] class AddSomeToDomainBlocks < ActiveRecord::Migration[6.1]
def change def change
safety_assured do
change_table :domain_blocks, bulk: true do |t| change_table :domain_blocks, bulk: true do |t|
t.boolean :reject_hashtag, null: false, default: false t.boolean :reject_hashtag, null: false, default: false
t.boolean :reject_straight_follow, null: false, default: false t.boolean :reject_straight_follow, null: false, default: false
t.boolean :reject_new_follow, null: false, default: false t.boolean :reject_new_follow, null: false, default: false
end end
end end
end
def down def down
safety_assured do
change_table :domain_blocks, bulk: true do |t| change_table :domain_blocks, bulk: true do |t|
t.remove :reject_hashtag t.remove :reject_hashtag
t.remove :reject_straight_follow t.remove :reject_straight_follow
@ -17,3 +20,4 @@ class AddSomeToDomainBlocks < ActiveRecord::Migration[6.1]
end end
end end
end end
end

View file

@ -2,16 +2,20 @@
class AddHiddenToDomainBlocks < ActiveRecord::Migration[6.1] class AddHiddenToDomainBlocks < ActiveRecord::Migration[6.1]
def change def change
safety_assured do
change_table :domain_blocks, bulk: true do |t| change_table :domain_blocks, bulk: true do |t|
t.boolean :hidden, null: false, default: false t.boolean :hidden, null: false, default: false
t.boolean :hidden_anonymous, null: false, default: false t.boolean :hidden_anonymous, null: false, default: false
end end
end end
end
def down def down
safety_assured do
change_table :domain_blocks, bulk: true do |t| change_table :domain_blocks, bulk: true do |t|
t.remove :hidden t.remove :hidden
t.remove :hidden_anonymous t.remove :hidden_anonymous
end end
end end
end end
end

View file

@ -2,6 +2,8 @@
class AddEmojiReactionStreamingToAccounts < ActiveRecord::Migration[6.1] class AddEmojiReactionStreamingToAccounts < ActiveRecord::Migration[6.1]
def change def change
safety_assured do
add_column :accounts, :stop_emoji_reaction_streaming, :boolean, default: false add_column :accounts, :stop_emoji_reaction_streaming, :boolean, default: false
end end
end end
end

View file

@ -1,10 +1,23 @@
# frozen_string_literal: true # frozen_string_literal: true
class AddRejectInvalidSubscriptionToDomainBlocks < ActiveRecord::Migration[6.1] class AddRejectInvalidSubscriptionToDomainBlocks < ActiveRecord::Migration[6.1]
def change def up
safety_assured do safety_assured do
remove_column :domain_blocks, :reject_send_unlisted_dissubscribable, :boolean, null: false, default: false remove_column :domain_blocks, :reject_send_unlisted_dissubscribable
end
add_column :domain_blocks, :detect_invalid_subscription, :boolean, null: false, default: false change_table :domain_blocks do |t|
t.boolean :detect_invalid_subscription, null: false, default: false
end
end
end
def down
safety_assured do
remove_column :domain_blocks, :detect_invalid_subscription
change_table :domain_blocks do |t|
t.boolean :reject_send_unlisted_dissubscribable, null: false, default: false
end
end
end end
end end

View file

@ -2,6 +2,10 @@
class AddRejectReplyExcludeFollowersToDomainBlocks < ActiveRecord::Migration[6.1] class AddRejectReplyExcludeFollowersToDomainBlocks < ActiveRecord::Migration[6.1]
def change def change
add_column :domain_blocks, :reject_reply_exclude_followers, :boolean, null: false, default: false safety_assured do
change_table :domain_blocks do |t|
t.boolean :reject_reply_exclude_followers, null: false, default: false
end
end
end end
end end

View file

@ -2,6 +2,10 @@
class AddIsSensitiveToCustomEmojis < ActiveRecord::Migration[6.1] class AddIsSensitiveToCustomEmojis < ActiveRecord::Migration[6.1]
def change def change
add_column :custom_emojis, :is_sensitive, :boolean, null: false, default: false safety_assured do
change_table :custom_emojis do |t|
t.boolean :is_sensitive, null: false, default: false
end
end
end end
end end

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
class CreateInstanceInfoes < ActiveRecord::Migration[7.0]
def change
create_table :instance_infos do |t|
t.string :domain, null: false, default: '', index: { unique: true }
t.string :software, null: false, default: ''
t.string :version, null: false, default: ''
t.jsonb :data, null: false, default: {}
t.datetime :created_at, null: false
t.datetime :updated_at, null: false
end
end
end

View file

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2023_07_24_160715) do ActiveRecord::Schema[7.0].define(version: 2023_08_04_222017) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -631,6 +631,16 @@ ActiveRecord::Schema[7.0].define(version: 2023_07_24_160715) do
t.boolean "overwrite", default: false, null: false t.boolean "overwrite", default: false, null: false
end end
create_table "instance_infos", force: :cascade do |t|
t.string "domain", default: "", null: false
t.string "software", default: "", null: false
t.string "version", default: "", null: false
t.jsonb "data", default: {}, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["domain"], name: "index_instance_infos_on_domain", unique: true
end
create_table "invites", force: :cascade do |t| create_table "invites", force: :cascade do |t|
t.bigint "user_id", null: false t.bigint "user_id", null: false
t.string "code", default: "", null: false t.string "code", default: "", null: false

View file

@ -26,7 +26,7 @@ RSpec.describe EmojiFormatter do
let(:text) { preformat_text(':coolcat: Beep boop') } let(:text) { preformat_text(':coolcat: Beep boop') }
it 'converts the shortcode to an image tag' do it 'converts the shortcode to an image tag' do
expect(subject).to match(/<img rel="emoji" draggable="false" width="16" height="16" class="emojione custom-emoji" alt=":coolcat:"/) expect(subject).to match(/<img rel="emoji" draggable="false" height="16" class="emojione custom-emoji" style="min-width:16px;" alt=":coolcat:"/)
end end
end end
@ -34,7 +34,7 @@ RSpec.describe EmojiFormatter do
let(:text) { preformat_text('Beep :coolcat: boop') } let(:text) { preformat_text('Beep :coolcat: boop') }
it 'converts the shortcode to an image tag' do it 'converts the shortcode to an image tag' do
expect(subject).to match(/Beep <img rel="emoji" draggable="false" width="16" height="16" class="emojione custom-emoji" alt=":coolcat:"/) expect(subject).to match(/Beep <img rel="emoji" draggable="false" height="16" class="emojione custom-emoji" style="min-width:16px;" alt=":coolcat:"/)
end end
end end
@ -50,7 +50,7 @@ RSpec.describe EmojiFormatter do
let(:text) { preformat_text('Beep boop :coolcat:') } let(:text) { preformat_text('Beep boop :coolcat:') }
it 'converts the shortcode to an image tag' do it 'converts the shortcode to an image tag' do
expect(subject).to match(/boop <img rel="emoji" draggable="false" width="16" height="16" class="emojione custom-emoji" alt=":coolcat:"/) expect(subject).to match(/boop <img rel="emoji" draggable="false" height="16" class="emojione custom-emoji" style="min-width:16px;" alt=":coolcat:"/)
end end
end end
end end

View file

@ -9,7 +9,7 @@ describe SearchQueryTransformer do
it 'sets attributes' do it 'sets attributes' do
transformer = described_class.new.apply(parser) transformer = described_class.new.apply(parser)
expect(transformer.should_clauses.first).to be_a(SearchQueryTransformer::TermClause) expect(transformer.should_clauses.first).to be_a(SearchQueryTransformer::PhraseClause)
expect(transformer.must_clauses.first).to be_nil expect(transformer.must_clauses.first).to be_nil
expect(transformer.must_not_clauses.first).to be_nil expect(transformer.must_not_clauses.first).to be_nil
expect(transformer.filter_clauses.first).to be_nil expect(transformer.filter_clauses.first).to be_nil

View file

@ -843,18 +843,18 @@ RSpec.describe Account do
describe 'matches_display_name' do describe 'matches_display_name' do
it 'matches display name which starts with the given string' do it 'matches display name which starts with the given string' do
match = Fabricate(:account, display_name: 'pattern and suffix') match = Fabricate(:account, display_name: 'pattern and suffix')
Fabricate(:account, display_name: 'prefix and pattern') account = Fabricate(:account, display_name: 'prefix and pattern')
expect(described_class.matches_display_name('pattern')).to eq [match] expect(described_class.matches_display_name('pattern')).to eq [match, account]
end end
end end
describe 'matches_username' do describe 'matches_username' do
it 'matches display name which starts with the given string' do it 'matches display name which starts with the given string' do
match = Fabricate(:account, username: 'pattern_and_suffix') match = Fabricate(:account, username: 'pattern_and_suffix')
Fabricate(:account, username: 'prefix_and_pattern') account = Fabricate(:account, username: 'prefix_and_pattern')
expect(described_class.matches_username('pattern')).to eq [match] expect(described_class.matches_username('pattern')).to eq [match, account]
end end
end end

View file

@ -60,10 +60,38 @@ RSpec.describe 'Domain Blocks' do
end end
end end
let(:expected_responde_with_kb_custom) do
domain_blocks.map do |domain_block|
{
id: domain_block.id.to_s,
domain: domain_block.domain,
created_at: domain_block.created_at.strftime('%Y-%m-%dT%H:%M:%S.%LZ'),
severity: domain_block.severity.to_s,
reject_media: domain_block.reject_media,
reject_reports: domain_block.reject_reports,
private_comment: domain_block.private_comment,
public_comment: domain_block.public_comment,
obfuscate: domain_block.obfuscate,
reject_favourite: domain_block.reject_favourite,
reject_hashtag: domain_block.reject_hashtag,
detect_invalid_subscription: domain_block.detect_invalid_subscription,
reject_new_follow: domain_block.reject_new_follow,
reject_reply: domain_block.reject_reply,
reject_reply_exclude_followers: domain_block.reject_reply_exclude_followers,
reject_send_dissubscribable: domain_block.reject_send_dissubscribable,
reject_send_media: domain_block.reject_send_media,
reject_send_not_public_searchability: domain_block.reject_send_not_public_searchability,
reject_send_public_unlisted: domain_block.reject_send_public_unlisted,
reject_send_sensitive: domain_block.reject_send_sensitive,
reject_straight_follow: domain_block.reject_straight_follow,
}
end
end
it 'returns the expected domain blocks' do it 'returns the expected domain blocks' do
subject subject
expect(body_as_json).to match_array(expected_responde) expect(body_as_json).to match_array(expected_responde_with_kb_custom)
end end
context 'with limit param' do context 'with limit param' do
@ -85,6 +113,32 @@ RSpec.describe 'Domain Blocks' do
let!(:domain_block) { Fabricate(:domain_block) } let!(:domain_block) { Fabricate(:domain_block) }
let(:expected_response) do
{
id: domain_block.id.to_s,
domain: domain_block.domain,
created_at: domain_block.created_at.strftime('%Y-%m-%dT%H:%M:%S.%LZ'),
severity: domain_block.severity.to_s,
reject_media: domain_block.reject_media,
reject_reports: domain_block.reject_reports,
private_comment: domain_block.private_comment,
public_comment: domain_block.public_comment,
obfuscate: domain_block.obfuscate,
reject_favourite: domain_block.reject_favourite,
reject_hashtag: domain_block.reject_hashtag,
detect_invalid_subscription: domain_block.detect_invalid_subscription,
reject_new_follow: domain_block.reject_new_follow,
reject_reply: domain_block.reject_reply,
reject_reply_exclude_followers: domain_block.reject_reply_exclude_followers,
reject_send_dissubscribable: domain_block.reject_send_dissubscribable,
reject_send_media: domain_block.reject_send_media,
reject_send_not_public_searchability: domain_block.reject_send_not_public_searchability,
reject_send_public_unlisted: domain_block.reject_send_public_unlisted,
reject_send_sensitive: domain_block.reject_send_sensitive,
reject_straight_follow: domain_block.reject_straight_follow,
}
end
it_behaves_like 'forbidden for wrong scope', 'write:statuses' it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong role', '' it_behaves_like 'forbidden for wrong role', ''
it_behaves_like 'forbidden for wrong role', 'Moderator' it_behaves_like 'forbidden for wrong role', 'Moderator'
@ -98,19 +152,7 @@ RSpec.describe 'Domain Blocks' do
it 'returns the expected domain block content' do it 'returns the expected domain block content' do
subject subject
expect(body_as_json).to eq( expect(body_as_json).to eq(expected_response)
{
id: domain_block.id.to_s,
domain: domain_block.domain,
created_at: domain_block.created_at.strftime('%Y-%m-%dT%H:%M:%S.%LZ'),
severity: domain_block.severity.to_s,
reject_media: domain_block.reject_media,
reject_reports: domain_block.reject_reports,
private_comment: domain_block.private_comment,
public_comment: domain_block.public_comment,
obfuscate: domain_block.obfuscate,
}
)
end end
context 'when the requested domain block does not exist' do context 'when the requested domain block does not exist' do

View file

@ -33,6 +33,18 @@ RSpec.describe 'Lists' do
end end
end end
let(:expected_response_with_antennas) do
lists.map do |list|
{
id: list.id.to_s,
title: list.title,
replies_policy: list.replies_policy,
exclusive: list.exclusive,
antennas: list.antennas,
}
end
end
before do before do
Fabricate(:list) Fabricate(:list)
end end
@ -48,7 +60,7 @@ RSpec.describe 'Lists' do
it 'returns the expected lists' do it 'returns the expected lists' do
subject subject
expect(body_as_json).to match_array(expected_response) expect(body_as_json).to match_array(expected_response_with_antennas)
end end
end end
@ -75,6 +87,7 @@ RSpec.describe 'Lists' do
title: list.title, title: list.title,
replies_policy: list.replies_policy, replies_policy: list.replies_policy,
exclusive: list.exclusive, exclusive: list.exclusive,
antennas: list.antennas,
}) })
end end
@ -171,6 +184,7 @@ RSpec.describe 'Lists' do
title: list.title, title: list.title,
replies_policy: list.replies_policy, replies_policy: list.replies_policy,
exclusive: list.exclusive, exclusive: list.exclusive,
antennas: list.antennas,
}) })
end end

View file

@ -21,7 +21,7 @@ describe 'Content-Security-Policy' do
"child-src 'self' blob: https://cb6e6126.ngrok.io", "child-src 'self' blob: https://cb6e6126.ngrok.io",
"worker-src 'self' blob: https://cb6e6126.ngrok.io", "worker-src 'self' blob: https://cb6e6126.ngrok.io",
"connect-src 'self' data: blob: https://cb6e6126.ngrok.io https://cb6e6126.ngrok.io ws://localhost:4000", "connect-src 'self' data: blob: https://cb6e6126.ngrok.io https://cb6e6126.ngrok.io ws://localhost:4000",
"script-src 'self' https://cb6e6126.ngrok.io 'wasm-unsafe-eval'" "script-src 'self' https://cb6e6126.ngrok.io 'wasm-unsafe-eval' https://www.googletagmanager.com https://googleads.g.doubleclick.net https://www.googleadservices.com https://www.google.co.jp https://www.google.com 'sha256-CS1WvLDd3zJOdxpEk+N+VigcWMa6V345p2HS0WYiFWE='"
) )
end end
end end