From d317663dfcb453086e37a0ba9860c4b2e193c418 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?KMY=EF=BC=88=E9=9B=AA=E3=81=82=E3=81=99=E3=81=8B=EF=BC=89?= Date: Tue, 31 Oct 2023 12:34:49 +0900 Subject: [PATCH 01/22] =?UTF-8?q?Fix:=20=E6=8A=95=E7=A8=BF=E3=81=AE?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E3=81=8C=E4=BB=96=E3=82=B5=E3=83=BC=E3=83=90?= =?UTF-8?q?=E3=83=BC=E3=81=AB=E9=85=8D=E4=BF=A1=E3=81=95=E3=82=8C=E3=81=AA?= =?UTF-8?q?=E3=81=84=E5=95=8F=E9=A1=8C=20(#204)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/workers/activitypub/status_update_distribution_worker.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/workers/activitypub/status_update_distribution_worker.rb b/app/workers/activitypub/status_update_distribution_worker.rb index a7081bed8e..49ac509761 100644 --- a/app/workers/activitypub/status_update_distribution_worker.rb +++ b/app/workers/activitypub/status_update_distribution_worker.rb @@ -22,7 +22,7 @@ class ActivityPub::StatusUpdateDistributionWorker < ActivityPub::DistributionWor actor: ActivityPub::TagManager.instance.uri_for(@status.account), published: @status.edited_at, to: for_friend ? ActivityPub::TagManager.instance.to_for_friend(@status) : ActivityPub::TagManager.instance.to(@status), - cc: for_misskey ? ActivityPub::TagManager.instance.cc_for_misskey : ActivityPub::TagManager.instance.cc(@status), + cc: for_misskey ? ActivityPub::TagManager.instance.cc_for_misskey(@status) : ActivityPub::TagManager.instance.cc(@status), virtual_object: @status ) end From ce0f3241e2d03caceda7fb7edf78ffe82a712f4f Mon Sep 17 00:00:00 2001 From: KMY Date: Tue, 31 Oct 2023 18:03:33 +0900 Subject: [PATCH 02/22] Bump version to 8.1 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 8d999a1cd7..7416de85ca 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -9,7 +9,7 @@ module Mastodon end def kmyblue_minor - 0 + 1 end def kmyblue_flag From d8740706a4f62a195a000d64442170ddec7b7761 Mon Sep 17 00:00:00 2001 From: KMY Date: Tue, 28 Nov 2023 12:40:03 +0900 Subject: [PATCH 03/22] =?UTF-8?q?Fix:=20=E3=83=9F=E3=83=A5=E3=83=BC?= =?UTF-8?q?=E3=83=88=E6=93=8D=E4=BD=9C=E6=99=82=E3=81=AE=E3=82=A8=E3=83=A9?= =?UTF-8?q?=E3=83=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/javascript/mastodon/reducers/notifications.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/reducers/notifications.js b/app/javascript/mastodon/reducers/notifications.js index 57e280a3bb..9764a22c76 100644 --- a/app/javascript/mastodon/reducers/notifications.js +++ b/app/javascript/mastodon/reducers/notifications.js @@ -284,7 +284,7 @@ export default function notifications(state = initialState, action) { case blockAccountSuccess.type: return filterNotifications(state, [action.payload.relationship.id]); case muteAccountSuccess.type: - return action.relationship.muting_notifications ? filterNotifications(state, [action.payload.relationship.id]) : state; + return action.payload.relationship.muting_notifications ? filterNotifications(state, [action.payload.relationship.id]) : state; case blockDomainSuccess.type: return filterNotifications(state, action.payload.accounts); case authorizeFollowRequestSuccess.type: From 795d2c7adf3cc8ce640ed642575843e91f8667d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?KMY=EF=BC=88=E9=9B=AA=E3=81=82=E3=81=99=E3=81=8B=EF=BC=89?= Date: Tue, 28 Nov 2023 12:38:04 +0900 Subject: [PATCH 04/22] =?UTF-8?q?Fix:=20#301=20=E3=82=AB=E3=82=B9=E3=82=BF?= =?UTF-8?q?=E3=83=A0=E7=B5=B5=E6=96=87=E5=AD=97=E7=B7=A8=E9=9B=86=E6=99=82?= =?UTF-8?q?=E3=80=81=E3=82=AB=E3=83=86=E3=82=B4=E3=83=AA=E9=81=B8=E6=8A=9E?= =?UTF-8?q?=E3=81=AE=E5=88=9D=E6=9C=9F=E5=80=A4=E3=81=AE=E7=95=B0=E5=B8=B8?= =?UTF-8?q?=20(#306)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/views/admin/custom_emojis/edit.html.haml | 2 +- app/views/admin/custom_emojis/new.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/admin/custom_emojis/edit.html.haml b/app/views/admin/custom_emojis/edit.html.haml index 212684f048..9752179f81 100644 --- a/app/views/admin/custom_emojis/edit.html.haml +++ b/app/views/admin/custom_emojis/edit.html.haml @@ -24,7 +24,7 @@ %h4= t('admin.custom_emojis.edit.label') .fields-group - = f.select :category_id, options_from_collection_for_select(CustomEmojiCategory.all, 'id', 'name'), prompt: t('admin.custom_emojis.assign_category'), class: 'select optional', 'aria-label': t('admin.custom_emojis.assign_category') + = f.input :category_id, collection: CustomEmojiCategory.all, label_method: ->(item) { item.name }, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', include_blank: t('admin.custom_emojis.assign_category'), wrapper: :with_label, label: false, hint: false .fields-group = f.input :visible_in_picker, as: :boolean, wrapper: :with_label, label: t('admin.custom_emojis.visible_in_picker') diff --git a/app/views/admin/custom_emojis/new.html.haml b/app/views/admin/custom_emojis/new.html.haml index 729c7a47a2..ed813b41eb 100644 --- a/app/views/admin/custom_emojis/new.html.haml +++ b/app/views/admin/custom_emojis/new.html.haml @@ -11,7 +11,7 @@ = f.input :image, wrapper: :with_label, input_html: { accept: CustomEmoji::IMAGE_MIME_TYPES.join(' ') }, hint: t('admin.custom_emojis.image_hint', size: number_to_human_size(CustomEmoji::LIMIT)) .fields-group - = f.select :category_id, options_from_collection_for_select(CustomEmojiCategory.all, 'id', 'name'), prompt: t('admin.custom_emojis.assign_category'), class: 'select optional', 'aria-label': t('admin.custom_emojis.assign_category') + = f.input :category_id, collection: CustomEmojiCategory.all, label_method: ->(item) { item.name }, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', include_blank: t('admin.custom_emojis.assign_category'), wrapper: :with_label, label: false, hint: false .fields-group = f.input :visible_in_picker, as: :boolean, wrapper: :with_label, label: t('admin.custom_emojis.visible_in_picker') From c21827f5ee6193dd3f4ce373fd04fe4aa986ab71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?KMY=EF=BC=88=E9=9B=AA=E3=81=82=E3=81=99=E3=81=8B=EF=BC=89?= Date: Wed, 29 Nov 2023 13:45:24 +0900 Subject: [PATCH 05/22] =?UTF-8?q?Fix:=20=E3=82=B9=E3=82=BF=E3=83=B3?= =?UTF-8?q?=E3=83=97=E3=82=92=E5=A4=96=E3=81=99=E3=81=A8=E3=81=8D=E3=80=81?= =?UTF-8?q?=E8=87=AA=E5=88=86=E3=81=8C=E5=A4=96=E3=81=97=E3=81=9F=E3=82=B9?= =?UTF-8?q?=E3=82=BF=E3=83=B3=E3=83=97=E3=81=8C=E6=8A=95=E7=A8=BF=E3=81=8B?= =?UTF-8?q?=E3=82=89=E5=85=A8=E9=83=A8=E6=B6=88=E3=81=88=E3=82=8B=E5=95=8F?= =?UTF-8?q?=E9=A1=8C=20(#316)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/status.rb | 2 ++ app/services/un_emoji_react_service.rb | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/models/status.rb b/app/models/status.rb index 0cb4c03a51..2b24ff50e5 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -405,6 +405,8 @@ class Status < ApplicationRecord end public_emoji_reactions + else + emoji_reactions end end end diff --git a/app/services/un_emoji_react_service.rb b/app/services/un_emoji_react_service.rb index 400efda19a..9dadfad68b 100644 --- a/app/services/un_emoji_react_service.rb +++ b/app/services/un_emoji_react_service.rb @@ -42,8 +42,8 @@ class UnEmojiReactService < BaseService end def write_stream(emoji_reaction) - emoji_group = @status.emoji_reactions_grouped_by_name - .find { |reaction_group| reaction_group['name'] == emoji_reaction.name && (!reaction_group.key?(:domain) || reaction_group['domain'] == emoji_reaction.custom_emoji&.domain) } + emoji_group = @status.emoji_reactions_grouped_by_name(@account) + .find { |reaction_group| reaction_group['name'] == emoji_reaction.name } if emoji_group emoji_group['status_id'] = @status.id.to_s else @@ -51,6 +51,7 @@ class UnEmojiReactService < BaseService emoji_group = { 'name' => emoji_reaction.name, 'count' => 0, 'account_ids' => [], 'status_id' => @status.id.to_s } emoji_group['domain'] = emoji_reaction.custom_emoji.domain if emoji_reaction.custom_emoji end + DeliveryEmojiReactionWorker.perform_async(render_emoji_reaction(emoji_group), @status.id, emoji_reaction.account_id) end From 18af335d69384a019ce465057c4f1ccf9cb69f97 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 16 Nov 2023 14:43:02 +0100 Subject: [PATCH 06/22] Fix upper border radius of onboarding columns (#27890) --- app/javascript/styles/mastodon/components.scss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 1f13334dee..0a331ca9d0 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -2371,8 +2371,7 @@ $ui-header-height: 55px; > .scrollable { background: $ui-base-color; - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; + border-radius: 0 0 4px 4px; } } From 6072ab23d76b594eef25b3e21ecf54942821af7d Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 27 Nov 2023 14:25:54 +0100 Subject: [PATCH 07/22] Clamp dates when serializing to Elasticsearch API (#28081) --- app/chewy/accounts_index.rb | 4 +++- app/chewy/concerns/datetime_clamping_concern.rb | 14 ++++++++++++++ app/chewy/public_statuses_index.rb | 4 +++- app/chewy/statuses_index.rb | 4 +++- app/chewy/tags_index.rb | 4 +++- 5 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 app/chewy/concerns/datetime_clamping_concern.rb diff --git a/app/chewy/accounts_index.rb b/app/chewy/accounts_index.rb index 9503fb85a1..fa89ac06a8 100644 --- a/app/chewy/accounts_index.rb +++ b/app/chewy/accounts_index.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class AccountsIndex < Chewy::Index + include DatetimeClampingConcern + DEVELOPMENT_SETTINGS = { filter: { english_stop: { @@ -153,7 +155,7 @@ class AccountsIndex < Chewy::Index field(:following_count, type: 'long', value: ->(account) { account.public_following_count }) field(:followers_count, type: 'long', value: ->(account) { account.public_followers_count }) field(:properties, type: 'keyword', value: ->(account) { account.searchable_properties }) - field(:last_status_at, type: 'date', value: ->(account) { account.last_status_at || account.created_at }) + field(:last_status_at, type: 'date', value: ->(account) { clamp_date(account.last_status_at || account.created_at) }) field(:domain, type: 'keyword', value: ->(account) { account.domain || '' }) field(:display_name, type: 'text', analyzer: 'verbatim') { field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'verbatim' } field(:username, type: 'text', analyzer: 'verbatim', value: ->(account) { [account.username, account.domain].compact.join('@') }) { field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'verbatim' } diff --git a/app/chewy/concerns/datetime_clamping_concern.rb b/app/chewy/concerns/datetime_clamping_concern.rb new file mode 100644 index 0000000000..7f176b6e54 --- /dev/null +++ b/app/chewy/concerns/datetime_clamping_concern.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module DatetimeClampingConcern + extend ActiveSupport::Concern + + MIN_ISO8601_DATETIME = '0000-01-01T00:00:00Z'.to_datetime.freeze + MAX_ISO8601_DATETIME = '9999-12-31T23:59:59Z'.to_datetime.freeze + + class_methods do + def clamp_date(datetime) + datetime.clamp(MIN_ISO8601_DATETIME, MAX_ISO8601_DATETIME) + end + end +end diff --git a/app/chewy/public_statuses_index.rb b/app/chewy/public_statuses_index.rb index fc58f8f097..0d53eb2f41 100644 --- a/app/chewy/public_statuses_index.rb +++ b/app/chewy/public_statuses_index.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class PublicStatusesIndex < Chewy::Index + include DatetimeClampingConcern + DEVELOPMENT_SETTINGS = { filter: { english_stop: { @@ -154,6 +156,6 @@ class PublicStatusesIndex < Chewy::Index field(:language, type: 'keyword') field(:domain, type: 'keyword', value: ->(status) { status.account.domain || '' }) field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties }) - field(:created_at, type: 'date') + field(:created_at, type: 'date', value: ->(status) { clamp_date(status.created_at) }) end end diff --git a/app/chewy/statuses_index.rb b/app/chewy/statuses_index.rb index 6a6621fb8c..647b35c001 100644 --- a/app/chewy/statuses_index.rb +++ b/app/chewy/statuses_index.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class StatusesIndex < Chewy::Index + include DatetimeClampingConcern + DEVELOPMENT_SETTINGS = { filter: { english_stop: { @@ -184,6 +186,6 @@ class StatusesIndex < Chewy::Index field(:language, type: 'keyword') field(:domain, type: 'keyword', value: ->(status) { status.account.domain || '' }) field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties }) - field(:created_at, type: 'date') + field(:created_at, type: 'date', value: ->(status) { clamp_date(status.created_at) }) end end diff --git a/app/chewy/tags_index.rb b/app/chewy/tags_index.rb index 5b6349a964..c99218a47f 100644 --- a/app/chewy/tags_index.rb +++ b/app/chewy/tags_index.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class TagsIndex < Chewy::Index + include DatetimeClampingConcern + settings index: index_preset(refresh_interval: '30s'), analysis: { analyzer: { content: { @@ -42,6 +44,6 @@ class TagsIndex < Chewy::Index field(:name, type: 'text', analyzer: 'content', value: :display_name) { field(:edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content') } field(:reviewed, type: 'boolean', value: ->(tag) { tag.reviewed? }) field(:usage, type: 'long', value: ->(tag, crutches) { tag.history.aggregate(crutches.time_period).accounts }) - field(:last_status_at, type: 'date', value: ->(tag) { tag.last_status_at || tag.created_at }) + field(:last_status_at, type: 'date', value: ->(tag) { clamp_date(tag.last_status_at || tag.created_at) }) end end From 78feb203d84e3a9a0f232f2a0b6a7e240ddd6123 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 24 Nov 2023 10:31:28 +0100 Subject: [PATCH 08/22] Change GIF max matrix size error to explicitly mention GIF files (#27927) --- app/models/concerns/attachmentable.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/models/concerns/attachmentable.rb b/app/models/concerns/attachmentable.rb index c0ee1bdce7..4cdbdeb473 100644 --- a/app/models/concerns/attachmentable.rb +++ b/app/models/concerns/attachmentable.rb @@ -52,9 +52,13 @@ module Attachmentable return if attachment.blank? || !/image.*/.match?(attachment.content_type) || attachment.queued_for_write[:original].blank? width, height = FastImage.size(attachment.queued_for_write[:original].path) - matrix_limit = attachment.content_type == 'image/gif' ? GIF_MATRIX_LIMIT : MAX_MATRIX_LIMIT + return unless width.present? && height.present? - raise Mastodon::DimensionsValidationError, "#{width}x#{height} images are not supported" if width.present? && height.present? && (width * height > matrix_limit) + if attachment.content_type == 'image/gif' && width * height > GIF_MATRIX_LIMIT + raise Mastodon::DimensionsValidationError, "#{width}x#{height} GIF files are not supported" + elsif width * height > MAX_MATRIX_LIMIT + raise Mastodon::DimensionsValidationError, "#{width}x#{height} images are not supported" + end end def appropriate_extension(attachment) From 6e34ce6826f8e3a48a93803bdd23eb8fb8434b9e Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 30 Nov 2023 12:45:54 +0100 Subject: [PATCH 09/22] Change dismissed banners to be stored server-side (#27055) --- .../components/dismissable_banner.tsx | 25 ++++++++++++++++--- app/javascript/mastodon/reducers/settings.js | 9 +++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/app/javascript/mastodon/components/dismissable_banner.tsx b/app/javascript/mastodon/components/dismissable_banner.tsx index 4feb74a3a1..4e6d3bb9a7 100644 --- a/app/javascript/mastodon/components/dismissable_banner.tsx +++ b/app/javascript/mastodon/components/dismissable_banner.tsx @@ -1,11 +1,18 @@ +/* eslint-disable @typescript-eslint/no-unsafe-call, + @typescript-eslint/no-unsafe-return, + @typescript-eslint/no-unsafe-assignment, + @typescript-eslint/no-unsafe-member-access + -- the settings store is not yet typed */ import type { PropsWithChildren } from 'react'; -import { useCallback, useState } from 'react'; +import { useCallback, useState, useEffect } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg'; +import { changeSetting } from 'mastodon/actions/settings'; import { bannerSettings } from 'mastodon/settings'; +import { useAppSelector, useAppDispatch } from 'mastodon/store'; import { IconButton } from './icon_button'; @@ -21,13 +28,25 @@ export const DismissableBanner: React.FC> = ({ id, children, }) => { - const [visible, setVisible] = useState(!bannerSettings.get(id)); + const dismissed = useAppSelector((state) => + state.settings.getIn(['dismissed_banners', id], false), + ); + const dispatch = useAppDispatch(); + + const [visible, setVisible] = useState(!bannerSettings.get(id) && !dismissed); const intl = useIntl(); const handleDismiss = useCallback(() => { setVisible(false); bannerSettings.set(id, true); - }, [id]); + dispatch(changeSetting(['dismissed_banners', id], true)); + }, [id, dispatch]); + + useEffect(() => { + if (!visible && !dismissed) { + dispatch(changeSetting(['dismissed_banners', id], true)); + } + }, [id, dispatch, visible, dismissed]); if (!visible) { return null; diff --git a/app/javascript/mastodon/reducers/settings.js b/app/javascript/mastodon/reducers/settings.js index fcb75bfb74..cc3fa0ec57 100644 --- a/app/javascript/mastodon/reducers/settings.js +++ b/app/javascript/mastodon/reducers/settings.js @@ -113,6 +113,15 @@ const initialState = ImmutableMap({ body: '', }), }), + + dismissed_banners: ImmutableMap({ + 'public_timeline': false, + 'community_timeline': false, + 'home.explore_prompt': false, + 'explore/links': false, + 'explore/statuses': false, + 'explore/tags': false, + }), }); const defaultColumns = fromJS([ From 82de3ed32da68aa87bef7803f53798f0ff574b7e Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 27 Nov 2023 15:00:52 +0100 Subject: [PATCH 10/22] Bump version to v4.2.2 --- CHANGELOG.md | 27 +++++++++++++++++++++++++++ docker-compose.yml | 6 +++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f775fcfa8..d827b204da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,33 @@ All notable changes to this project will be documented in this file. +## [4.2.2] - 2023-12-04 + +### Changed + +- Change dismissed banners to be stored server-side ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27055)) +- Change GIF max matrix size error to explicitly mention GIF files ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27927)) +- Change `Follow` activities delivery to bypass availability check ([ShadowJonathan](https://github.com/mastodon/mastodon/pull/27586)) +- Change single-column navigation notice to be displayed outside of the logo container ([renchap](https://github.com/mastodon/mastodon/pull/27462), [renchap](https://github.com/mastodon/mastodon/pull/27476)) +- Change Content-Security-Policy to be tighter on media paths ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26889)) +- Change post language code to include country code when relevant ([gunchleoc](https://github.com/mastodon/mastodon/pull/27099), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27207)) + +### Fixed + +- Fix upper border radius of onboarding columns ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27890)) +- Fix incoming status creation date not being restricted to standard ISO8601 ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27655), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/28081)) +- Fix some posts from threads received out-of-order sometimes not being inserted into timelines ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27653)) +- Fix posts from force-sensitized accounts being able to trend ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27620)) +- Fix error when trying to delete already-deleted file with OpenStack Swift ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27569)) +- Fix batch attachment deletion when using OpenStack Swift ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27554)) +- Fix processing LDSigned activities from actors with unknown public keys ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27474)) +- Fix error and incorrect URLs in `/api/v1/accounts/:id/featured_tags` for remote accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27459)) +- Fix report processing notice not mentioning the report number when performing a custom action ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27442)) +- Fix handling of `inLanguage` attribute in preview card processing ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27423)) +- Fix own posts being removed from home timeline when unfollowing a used hashtag ([kmycode](https://github.com/mastodon/mastodon/pull/27391)) +- Fix some link anchors being recognized as hashtags ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27271), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27584)) +- Fix format-dependent redirects being cached regardless of requested format ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27634)) + ## [4.2.1] - 2023-10-10 ### Added diff --git a/docker-compose.yml b/docker-compose.yml index f7639d9867..11ac28cae1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -56,7 +56,7 @@ services: web: build: . - image: ghcr.io/mastodon/mastodon:v4.2.1 + image: ghcr.io/mastodon/mastodon:v4.2.2 restart: always env_file: .env.production command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000" @@ -77,7 +77,7 @@ services: streaming: build: . - image: ghcr.io/mastodon/mastodon:v4.2.1 + image: ghcr.io/mastodon/mastodon:v4.2.2 restart: always env_file: .env.production command: node ./streaming @@ -95,7 +95,7 @@ services: sidekiq: build: . - image: ghcr.io/mastodon/mastodon:v4.2.1 + image: ghcr.io/mastodon/mastodon:v4.2.2 restart: always env_file: .env.production command: bundle exec sidekiq From e96bc706adc5304118639ae8b567d729e41750ff Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Dec 2023 13:57:16 +0100 Subject: [PATCH 11/22] Update dependency json-ld to v3.3.1 (#28229) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 57dedfd55d..f97ecd8802 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -373,16 +373,16 @@ GEM rdoc reline (>= 0.3.8) jmespath (1.6.2) - json (2.6.3) - json-canonicalization (0.3.2) + json (2.7.0) + json-canonicalization (1.0.0) json-jwt (1.15.3) activesupport (>= 4.2) aes_key_wrap bindata httpclient - json-ld (3.3.0) + json-ld (3.3.1) htmlentities (~> 4.3) - json-canonicalization (~> 0.3, >= 0.3.2) + json-canonicalization (~> 1.0) link_header (~> 0.0, >= 0.0.8) multi_json (~> 1.15) rack (>= 2.2, < 4) From 696403525a7c6a159da0757457165fc09860ace8 Mon Sep 17 00:00:00 2001 From: KMY Date: Wed, 6 Dec 2023 08:13:42 +0900 Subject: [PATCH 12/22] Bump version to 9.2 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index b6e9f72556..4435a5cb58 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -9,7 +9,7 @@ module Mastodon end def kmyblue_minor - 1 + 2 end def kmyblue_flag From f84dae2db658b5698d2112ff12431830f949e07a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?KMY=EF=BC=88=E9=9B=AA=E3=81=82=E3=81=99=E3=81=8B=EF=BC=89?= Date: Thu, 7 Dec 2023 10:06:07 +0900 Subject: [PATCH 13/22] =?UTF-8?q?Fix:=20=E4=B8=80=E9=83=A8=E7=92=B0?= =?UTF-8?q?=E5=A2=83=E3=81=A7=E3=83=9E=E3=82=A4=E3=82=B0=E3=83=AC=E3=83=BC?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=A7=E3=81=8D=E3=81=AA=E3=81=84?= =?UTF-8?q?=E5=95=8F=E9=A1=8C=20(#336)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix: 一部環境でマイグレーションできない問題 * Fix * Fix * Add settings test --- ...5225839_add_master_settings_to_accounts.rb | 28 +++---------------- .../process_account_service_spec.rb | 26 +++++++++++++++++ 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/db/migrate/20231105225839_add_master_settings_to_accounts.rb b/db/migrate/20231105225839_add_master_settings_to_accounts.rb index 25f6886c6a..24e3e107f9 100644 --- a/db/migrate/20231105225839_add_master_settings_to_accounts.rb +++ b/db/migrate/20231105225839_add_master_settings_to_accounts.rb @@ -13,18 +13,8 @@ class AddMasterSettingsToAccounts < ActiveRecord::Migration[7.1] safety_assured do add_column :accounts, :master_settings, :jsonb - if Rails.env.test? - Account.transaction do - Account.find_in_batches do |accounts| - accounts.each do |account| - account.update(master_settings: { 'subscription_policy' => account.dissubscribable ? 'block' : 'allow' }) - end - end - end - else - Account.where(dissubscribable: true).update_all(master_settings: { 'subscription_policy' => 'block' }) # rubocop:disable Rails/SkipsModelValidations - Account.where(dissubscribable: false).update_all(master_settings: { 'subscription_policy' => 'allow' }) # rubocop:disable Rails/SkipsModelValidations - end + ActiveRecord::Base.connection.execute("UPDATE accounts SET master_settings = json_build_object('subscription_policy', 'block') WHERE accounts.dissubscribable IS TRUE") + ActiveRecord::Base.connection.execute("UPDATE accounts SET master_settings = json_build_object('subscription_policy', 'allow') WHERE accounts.dissubscribable IS FALSE") remove_column :accounts, :dissubscribable end @@ -34,18 +24,8 @@ class AddMasterSettingsToAccounts < ActiveRecord::Migration[7.1] safety_assured do add_column_with_default :accounts, :dissubscribable, :boolean, default: false, allow_null: false - if Rails.env.test? - Account.transaction do - Account.find_in_batches do |accounts| - accounts.each do |account| - account.update(dissubscribable: account.master_settings.present? && account.master_settings['subscription_policy'] != 'allow') - end - end - end - else - Account.where(master_settings: { subscription_policy: 'block' }).update_all(dissubscribable: true) # rubocop:disable Rails/SkipsModelValidations - Account.where(master_settings: { subscription_policy: 'allow' }).update_all(dissubscribable: false) # rubocop:disable Rails/SkipsModelValidations - end + ActiveRecord::Base.connection.execute("UPDATE accounts SET dissubscribable = TRUE WHERE master_settings ->> 'subscription_policy' = 'block'") + ActiveRecord::Base.connection.execute("UPDATE accounts SET dissubscribable = FALSE WHERE master_settings ->> 'subscription_policy' = 'allow'") remove_column :accounts, :master_settings end diff --git a/spec/services/activitypub/process_account_service_spec.rb b/spec/services/activitypub/process_account_service_spec.rb index 40644db509..f0885ff672 100644 --- a/spec/services/activitypub/process_account_service_spec.rb +++ b/spec/services/activitypub/process_account_service_spec.rb @@ -252,6 +252,32 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do end end + context 'with other settings' do + let(:payload) do + { + id: 'https://foo.test', + type: 'Actor', + inbox: 'https://foo.test/inbox', + otherSetting: [ + { type: 'PropertyValue', name: 'Pronouns', value: 'They/them' }, + { type: 'PropertyValue', name: 'Occupation', value: 'Unit test' }, + ], + }.with_indifferent_access + end + + before do + stub_request(:get, 'https://example.com/.well-known/nodeinfo').to_return(body: '{}') + end + + it 'parses out of attachment' do + account = subject.call('alice', 'example.com', payload) + expect(account.settings).to be_a Hash + expect(account.settings.size).to eq 2 + expect(account.settings['Pronouns']).to eq 'They/them' + expect(account.settings['Occupation']).to eq 'Unit test' + end + end + context 'when account is not suspended' do subject { described_class.new.call('alice', 'example.com', payload) } From fdecd967bfb94615164f8a2042efc49e60ad054f Mon Sep 17 00:00:00 2001 From: KMY Date: Thu, 7 Dec 2023 10:07:30 +0900 Subject: [PATCH 14/22] Bump version to 9.3 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 4435a5cb58..c0f90aad19 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -9,7 +9,7 @@ module Mastodon end def kmyblue_minor - 2 + 3 end def kmyblue_flag From 0838f1ac8f584f89757c4271a5144883b3eefb0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?KMY=EF=BC=88=E9=9B=AA=E3=81=82=E3=81=99=E3=81=8B=EF=BC=89?= Date: Mon, 11 Dec 2023 14:02:44 +0900 Subject: [PATCH 15/22] =?UTF-8?q?Add:=20Sharkey=E3=82=92=E3=82=B9=E3=82=BF?= =?UTF-8?q?=E3=83=B3=E3=83=97=E5=88=A9=E7=94=A8=E5=8F=AF=E8=83=BD=E3=81=AA?= =?UTF-8?q?=E3=82=B5=E3=83=BC=E3=83=90=E3=83=BC=E3=81=AB=E8=BF=BD=E5=8A=A0?= =?UTF-8?q?=20(#345)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/lib/activitypub/activity/follow.rb | 2 +- app/lib/status_reach_finder.rb | 2 +- app/models/instance_info.rb | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/lib/activitypub/activity/follow.rb b/app/lib/activitypub/activity/follow.rb index fdded98440..c8cf16f822 100644 --- a/app/lib/activitypub/activity/follow.rb +++ b/app/lib/activitypub/activity/follow.rb @@ -103,7 +103,7 @@ class ActivityPub::Activity::Follow < ActivityPub::Activity info = instance_info return false if info.nil? - %w(misskey calckey firefish meisskey cherrypick).include?(info.software) + %w(misskey calckey firefish meisskey cherrypick sharkey).include?(info.software) end def instance_info diff --git a/app/lib/status_reach_finder.rb b/app/lib/status_reach_finder.rb index 8f20b94675..5ce2ff17a4 100644 --- a/app/lib/status_reach_finder.rb +++ b/app/lib/status_reach_finder.rb @@ -216,7 +216,7 @@ class StatusReachFinder return [] if status.public_searchability? return [] unless (status.public_unlisted_visibility? && status.account.user&.setting_reject_public_unlisted_subscription) || (status.unlisted_visibility? && status.account.user&.setting_reject_unlisted_subscription) - from_info = InstanceInfo.where(software: %w(misskey calckey cherrypick)).pluck(:domain) + from_info = InstanceInfo.where(software: %w(misskey calckey cherrypick sharkey)).pluck(:domain) from_domain_block = DomainBlock.where(detect_invalid_subscription: true).pluck(:domain) (from_info + from_domain_block).uniq end diff --git a/app/models/instance_info.rb b/app/models/instance_info.rb index dcdc0aacac..79c70fe06f 100644 --- a/app/models/instance_info.rb +++ b/app/models/instance_info.rb @@ -21,6 +21,7 @@ class InstanceInfo < ApplicationRecord calckey cherrypick meisskey + sharkey firefish renedon fedibird From 9e22306574d0ebb59d3591e849ff25011f4f8518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?KMY=EF=BC=88=E9=9B=AA=E3=81=82=E3=81=99=E3=81=8B=EF=BC=89?= Date: Mon, 11 Dec 2023 14:13:33 +0900 Subject: [PATCH 16/22] =?UTF-8?q?Fix:=20DistributionWorker=E3=81=A7String?= =?UTF-8?q?=20to=20Integer=E3=81=AE=E3=82=A8=E3=83=A9=E3=83=BC=E3=81=8C?= =?UTF-8?q?=E5=87=BA=E3=82=8B=E5=95=8F=E9=A1=8C=20(#344)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix: DeliveryWorkerでString to Integerのエラーが出る問題 * Test: Mastodon v3向け --- app/models/instance_info.rb | 1 + spec/models/status_spec.rb | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/models/instance_info.rb b/app/models/instance_info.rb index 79c70fe06f..62da02a743 100644 --- a/app/models/instance_info.rb +++ b/app/models/instance_info.rb @@ -40,6 +40,7 @@ class InstanceInfo < ApplicationRecord return false if info.nil? return true if EMOJI_REACTION_AVAILABLE_SOFTWARES.include?(info['software']) + return false if info.data['metadata'].nil? || !info.data['metadata'].is_a?(Hash) features = info.data.dig('metadata', 'features') return false if features.nil? || !features.is_a?(Array) diff --git a/spec/models/status_spec.rb b/spec/models/status_spec.rb index 4e10f3d316..c1732376d9 100644 --- a/spec/models/status_spec.rb +++ b/spec/models/status_spec.rb @@ -463,7 +463,7 @@ RSpec.describe Status do describe '.emoji_reaction_availables_map' do subject { described_class.emoji_reaction_availables_map(domains) } - let(:domains) { %w(features_available.com features_unavailable.com features_invalid.com features_nil.com no_info.com mastodon.com misskey.com) } + let(:domains) { %w(features_available.com features_unavailable.com features_invalid.com features_nil.com no_info.com mastodon.com misskey.com old_mastodon.com) } before do Fabricate(:instance_info, domain: 'features_available.com', software: 'mastodon', data: { metadata: { features: ['emoji_reaction'] } }) @@ -472,6 +472,7 @@ RSpec.describe Status do Fabricate(:instance_info, domain: 'features_nil.com', software: 'mastodon', data: { metadata: { features: nil } }) Fabricate(:instance_info, domain: 'mastodon.com', software: 'mastodon') Fabricate(:instance_info, domain: 'misskey.com', software: 'misskey') + Fabricate(:instance_info, domain: 'old_mastodon.com', software: 'mastodon', data: { metadata: [] }) end it 'availables if features contains emoji_reaction' do @@ -497,6 +498,10 @@ RSpec.describe Status do it 'availables if misskey server' do expect(subject['misskey.com']).to be true end + + it 'unavailables if old mastodon server' do + expect(subject['old_mastodon.com']).to be false + end end describe '.tagged_with' do From 94070aa41ef5dbf5cc942d7768f59bdefd3aea69 Mon Sep 17 00:00:00 2001 From: KMY Date: Tue, 12 Dec 2023 09:32:34 +0900 Subject: [PATCH 17/22] =?UTF-8?q?Fix:=20=E7=94=BB=E5=83=8F=E3=81=AE?= =?UTF-8?q?=E4=B8=A6=E3=81=B3=E9=A0=86=E3=81=8C=E5=A4=89=E3=82=8F=E3=82=8B?= =?UTF-8?q?=E5=A0=B4=E5=90=88=20(#339)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix: 画像の並び順が変わる場合 * Fix --- app/models/status.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/status.rb b/app/models/status.rb index 2b24ff50e5..55f8755ee5 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -83,7 +83,7 @@ class Status < ApplicationRecord has_many :mentions, dependent: :destroy, inverse_of: :status has_many :mentioned_accounts, through: :mentions, source: :account, class_name: 'Account' has_many :active_mentions, -> { active }, class_name: 'Mention', inverse_of: :status - has_many :media_attachments, dependent: :nullify + has_many :media_attachments, -> { order('id asc') }, dependent: :nullify, inverse_of: false has_many :reference_objects, class_name: 'StatusReference', inverse_of: :status, dependent: :destroy has_many :references, through: :reference_objects, class_name: 'Status', source: :target_status has_many :referenced_by_status_objects, foreign_key: 'target_status_id', class_name: 'StatusReference', inverse_of: :target_status, dependent: :destroy From bea82f2279ec57089922900e284e74af467e487f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?KMY=EF=BC=88=E9=9B=AA=E3=81=82=E3=81=99=E3=81=8B=EF=BC=89?= Date: Tue, 12 Dec 2023 09:52:08 +0900 Subject: [PATCH 18/22] =?UTF-8?q?Fix:=20#284=20`FetchInstanceInfoWorker`?= =?UTF-8?q?=E3=81=8C=E5=8E=9F=E5=9B=A0=E3=81=A7Sidekiq=E3=81=AEJob?= =?UTF-8?q?=E3=81=8C=E8=A9=B0=E3=81=BE=E3=82=8B=E5=95=8F=E9=A1=8C=20(#342)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix: #284 `FetchInstanceInfoWorker`が原因でSidekiqのJobが詰まる問題 * Fix: InstanceInfoを取得するタイミング * Fix test * Fix test * Fix: HTTPコード * 調整 --- .../activitypub/process_account_service.rb | 5 +-- .../activitypub/fetch_instance_info_worker.rb | 34 +++++++++---------- .../update_instance_info_scheduler.rb | 15 -------- config/sidekiq.yml | 4 --- spec/lib/activitypub/activity/update_spec.rb | 1 + .../process_account_service_spec.rb | 4 +++ .../fetch_instance_info_worker_spec.rb | 20 +++++++++++ .../update_instance_info_scheduler_spec.rb | 19 ----------- 8 files changed, 44 insertions(+), 58 deletions(-) delete mode 100644 app/workers/scheduler/update_instance_info_scheduler.rb delete mode 100644 spec/workers/scheduler/update_instance_info_scheduler_spec.rb diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb index f3b1c84615..a1a07eae04 100644 --- a/app/services/activitypub/process_account_service.rb +++ b/app/services/activitypub/process_account_service.rb @@ -46,7 +46,6 @@ class ActivityPub::ProcessAccountService < BaseService end create_account - fetch_instance_info end update_account @@ -66,6 +65,8 @@ class ActivityPub::ProcessAccountService < BaseService check_links! if @account.fields.any?(&:requires_verification?) end + fetch_instance_info + @account rescue Oj::ParseError nil @@ -210,7 +211,7 @@ class ActivityPub::ProcessAccountService < BaseService end def fetch_instance_info - ActivityPub::FetchInstanceInfoWorker.perform_async(@account.domain) unless InstanceInfo.exists?(domain: @account.domain) + ActivityPub::FetchInstanceInfoWorker.perform_async(@account.domain) unless Rails.cache.exist?("fetch_instance_info:#{@account.domain}", expires_in: 1.day) end def actor_type diff --git a/app/workers/activitypub/fetch_instance_info_worker.rb b/app/workers/activitypub/fetch_instance_info_worker.rb index 57cbd97d10..bc9a1a4815 100644 --- a/app/workers/activitypub/fetch_instance_info_worker.rb +++ b/app/workers/activitypub/fetch_instance_info_worker.rb @@ -8,28 +8,32 @@ class ActivityPub::FetchInstanceInfoWorker sidekiq_options queue: 'push', retry: 2 - class Error < StandardError; end - class RequestError < Error; end - class DeadError < 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) + Rails.cache.fetch("fetch_instance_info:#{@instance.domain}", expires_in: 1.day, race_condition_ttl: 1.hour) do + fetch! end - rescue ActivityPub::FetchInstanceInfoWorker::DeadError + true end private + def fetch! + link = nodeinfo_link + return if link.nil? + + update_info!(link) + + true + rescue Mastodon::UnexpectedResponseError + true + end + def nodeinfo_link nodeinfo = fetch_json("https://#{@instance.domain}/.well-known/nodeinfo") return nil if nodeinfo.nil? || !nodeinfo.key?('links') @@ -63,15 +67,9 @@ class ActivityPub::FetchInstanceInfoWorker 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) + raise Mastodon::UnexpectedResponseError, response unless response_successful?(response) || response_error_unsalvageable?(response) - body_to_json(response.body_with_limit) - elsif [400, 401, 403, 404, 410].include?(response.code) - raise ActivityPub::FetchInstanceInfoWorker::DeadError, "Request for #{@instance.domain} returned HTTP #{response.code}" - else - raise ActivityPub::FetchInstanceInfoWorker::RequestError, "Request for #{@instance.domain} returned HTTP #{response.code}" - end + body_to_json(response.body_with_limit) end end diff --git a/app/workers/scheduler/update_instance_info_scheduler.rb b/app/workers/scheduler/update_instance_info_scheduler.rb deleted file mode 100644 index f5b2852859..0000000000 --- a/app/workers/scheduler/update_instance_info_scheduler.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -class Scheduler::UpdateInstanceInfoScheduler - include Sidekiq::Worker - - sidekiq_options retry: 0, lock: :until_executed, lock_ttl: 1.day.to_i - - def perform - Instance.select(:domain).reorder(nil).find_in_batches do |instances| - ActivityPub::FetchInstanceInfoWorker.push_bulk(instances) do |instance| - [instance.domain] - end - end - end -end diff --git a/config/sidekiq.yml b/config/sidekiq.yml index 6102dcaeaf..538987c9ac 100644 --- a/config/sidekiq.yml +++ b/config/sidekiq.yml @@ -63,10 +63,6 @@ interval: 30 seconds class: Scheduler::SidekiqHealthScheduler queue: scheduler - update_instance_info_scheduler: - cron: '0 0 * * *' - class: Scheduler::UpdateInstanceInfoScheduler - queue: scheduler software_update_check_scheduler: interval: 30 minutes class: Scheduler::SoftwareUpdateCheckScheduler diff --git a/spec/lib/activitypub/activity/update_spec.rb b/spec/lib/activitypub/activity/update_spec.rb index 87e96d2d1b..6c84c5836a 100644 --- a/spec/lib/activitypub/activity/update_spec.rb +++ b/spec/lib/activitypub/activity/update_spec.rb @@ -55,6 +55,7 @@ RSpec.describe ActivityPub::Activity::Update do stub_request(:get, actor_json[:following]).to_return(status: 404) stub_request(:get, actor_json[:featured]).to_return(status: 404) stub_request(:get, actor_json[:featuredTags]).to_return(status: 404) + stub_request(:get, 'https://example.com/.well-known/nodeinfo').to_return(status: 404) subject.perform end diff --git a/spec/services/activitypub/process_account_service_spec.rb b/spec/services/activitypub/process_account_service_spec.rb index f0885ff672..6dc3ced27e 100644 --- a/spec/services/activitypub/process_account_service_spec.rb +++ b/spec/services/activitypub/process_account_service_spec.rb @@ -5,6 +5,10 @@ require 'rails_helper' RSpec.describe ActivityPub::ProcessAccountService, type: :service do subject { described_class.new } + before do + stub_request(:get, 'https://example.com/.well-known/nodeinfo').to_return(status: 404) + end + context 'with searchability' do subject { described_class.new.call('alice', 'example.com', payload) } diff --git a/spec/workers/activitypub/fetch_instance_info_worker_spec.rb b/spec/workers/activitypub/fetch_instance_info_worker_spec.rb index f6dacff5fc..9dc9594041 100644 --- a/spec/workers/activitypub/fetch_instance_info_worker_spec.rb +++ b/spec/workers/activitypub/fetch_instance_info_worker_spec.rb @@ -67,9 +67,22 @@ describe ActivityPub::FetchInstanceInfoWorker do Instance.refresh end + it 'does not update immediately' do + stub_request(:get, 'https://example.com/nodeinfo/2.0').to_return(status: 200, body: nodeinfo_json) + subject.perform('example.com') + stub_request(:get, 'https://example.com/nodeinfo/2.0').to_return(status: 200, body: new_nodeinfo_json) + subject.perform('example.com') + + info = InstanceInfo.find_by(domain: 'example.com') + expect(info).to_not be_nil + expect(info.software).to eq 'mastodon' + expect(info.version).to eq '4.2.0-beta1' + end + it 'performs a mastodon instance' do stub_request(:get, 'https://example.com/nodeinfo/2.0').to_return(status: 200, body: nodeinfo_json) subject.perform('example.com') + Rails.cache.delete('fetch_instance_info:example.com') stub_request(:get, 'https://example.com/nodeinfo/2.0').to_return(status: 200, body: new_nodeinfo_json) subject.perform('example.com') @@ -93,5 +106,12 @@ describe ActivityPub::FetchInstanceInfoWorker do info = InstanceInfo.find_by(domain: 'example.com') expect(info).to be_nil end + + it 'does not fetch again immediately' do + expect(subject.perform('example.com')).to be true + expect(subject.perform('example.com')).to be true + + expect(a_request(:get, 'https://example.com/.well-known/nodeinfo')).to have_been_made.once + end end end diff --git a/spec/workers/scheduler/update_instance_info_scheduler_spec.rb b/spec/workers/scheduler/update_instance_info_scheduler_spec.rb deleted file mode 100644 index f3a190417f..0000000000 --- a/spec/workers/scheduler/update_instance_info_scheduler_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe Scheduler::UpdateInstanceInfoScheduler do - let(:worker) { described_class.new } - - before do - stub_request(:get, 'https://example.com/.well-known/nodeinfo').to_return(status: 200, body: '{}') - Fabricate(:account, domain: 'example.com') - Instance.refresh - end - - describe 'perform' do - it 'runs without error' do - expect { worker.perform }.to_not raise_error - end - end -end From acdf1ef2c9ff8c70290f8a8369c8061e49dfa2f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?KMY=EF=BC=88=E9=9B=AA=E3=81=82=E3=81=99=E3=81=8B=EF=BC=89?= Date: Wed, 13 Dec 2023 09:13:32 +0900 Subject: [PATCH 19/22] =?UTF-8?q?Fix:=20#355=20LTL=E3=81=AE=E3=82=A4?= =?UTF-8?q?=E3=83=B3=E3=83=87=E3=83=83=E3=82=AF=E3=82=B9=E3=81=8C=E9=81=A9?= =?UTF-8?q?=E5=88=87=E3=81=AB=E3=81=AF=E3=82=89=E3=82=8C=E3=81=A6=E3=81=84?= =?UTF-8?q?=E3=81=AA=E3=81=84=E5=95=8F=E9=A1=8C=20(#357)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix: #355 LTLのインデックスが適切にはられていない問題 * Migrate --- ...improve_index_for_public_timeline_speed.rb | 19 +++++++++++++++++++ db/schema.rb | 6 +++--- 2 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 db/migrate/20231212225737_improve_index_for_public_timeline_speed.rb diff --git a/db/migrate/20231212225737_improve_index_for_public_timeline_speed.rb b/db/migrate/20231212225737_improve_index_for_public_timeline_speed.rb new file mode 100644 index 0000000000..bcfe83de03 --- /dev/null +++ b/db/migrate/20231212225737_improve_index_for_public_timeline_speed.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class ImproveIndexForPublicTimelineSpeed < ActiveRecord::Migration[7.0] + disable_ddl_transaction! + + def up + add_index :statuses, [:id, :account_id], name: :index_statuses_local_20231213, algorithm: :concurrently, order: { id: :desc }, where: '(local OR (uri IS NULL)) AND deleted_at IS NULL AND visibility IN (0, 10, 11) AND reblog_of_id IS NULL AND ((NOT reply) OR (in_reply_to_account_id = account_id))' + add_index :statuses, [:id, :account_id], name: :index_statuses_public_20231213, algorithm: :concurrently, order: { id: :desc }, where: 'deleted_at IS NULL AND visibility IN (0, 10, 11) AND reblog_of_id IS NULL AND ((NOT reply) OR (in_reply_to_account_id = account_id))' + remove_index :statuses, name: :index_statuses_local_20190824 + remove_index :statuses, name: :index_statuses_public_20200119 + end + + def down + add_index :statuses, [:id, :account_id], name: :index_statuses_local_20190824, algorithm: :concurrently, order: { id: :desc }, where: '(local OR (uri IS NULL)) AND deleted_at IS NULL AND visibility = 0 AND reblog_of_id IS NULL AND ((NOT reply) OR (in_reply_to_account_id = account_id))' + add_index :statuses, [:id, :account_id], name: :index_statuses_public_20200119, algorithm: :concurrently, order: { id: :desc }, where: 'deleted_at IS NULL AND visibility = 0 AND reblog_of_id IS NULL AND ((NOT reply) OR (in_reply_to_account_id = account_id))' + remove_index :statuses, name: :index_statuses_local_20231213 + remove_index :statuses, name: :index_statuses_public_20231213 + end +end diff --git a/db/schema.rb b/db/schema.rb index 4850882e11..62e7ed98fb 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2023_11_05_225839) do +ActiveRecord::Schema[7.1].define(version: 2023_12_12_225737) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -1235,8 +1235,8 @@ ActiveRecord::Schema[7.1].define(version: 2023_11_05_225839) do t.index ["account_id", "reblog_of_id", "deleted_at", "searchability"], name: "index_statuses_for_get_following_accounts_to_search", where: "((deleted_at IS NULL) AND (reblog_of_id IS NULL) AND (searchability = ANY (ARRAY[0, 10, 1])))" t.index ["account_id"], name: "index_statuses_on_account_id" t.index ["deleted_at"], name: "index_statuses_on_deleted_at", where: "(deleted_at IS NOT NULL)" - t.index ["id", "account_id"], name: "index_statuses_local_20190824", order: { id: :desc }, where: "((local OR (uri IS NULL)) AND (deleted_at IS NULL) AND (visibility = 0) AND (reblog_of_id IS NULL) AND ((NOT reply) OR (in_reply_to_account_id = account_id)))" - t.index ["id", "account_id"], name: "index_statuses_public_20200119", order: { id: :desc }, where: "((deleted_at IS NULL) AND (visibility = 0) AND (reblog_of_id IS NULL) AND ((NOT reply) OR (in_reply_to_account_id = account_id)))" + t.index ["id", "account_id"], name: "index_statuses_local_20231213", order: { id: :desc }, where: "((local OR (uri IS NULL)) AND (deleted_at IS NULL) AND (visibility = ANY (ARRAY[0, 10, 11])) AND (reblog_of_id IS NULL) AND ((NOT reply) OR (in_reply_to_account_id = account_id)))" + t.index ["id", "account_id"], name: "index_statuses_public_20231213", order: { id: :desc }, where: "((deleted_at IS NULL) AND (visibility = ANY (ARRAY[0, 10, 11])) AND (reblog_of_id IS NULL) AND ((NOT reply) OR (in_reply_to_account_id = account_id)))" t.index ["in_reply_to_account_id"], name: "index_statuses_on_in_reply_to_account_id", where: "(in_reply_to_account_id IS NOT NULL)" t.index ["in_reply_to_id"], name: "index_statuses_on_in_reply_to_id", where: "(in_reply_to_id IS NOT NULL)" t.index ["quote_of_id", "account_id"], name: "index_statuses_on_quote_of_id_and_account_id" From 7b847f4eb32f5279ee85538d3d52017d05fc76e2 Mon Sep 17 00:00:00 2001 From: KMY Date: Fri, 15 Dec 2023 08:21:36 +0900 Subject: [PATCH 20/22] =?UTF-8?q?Fix:=20=E4=B8=80=E9=83=A8=E3=82=B5?= =?UTF-8?q?=E3=83=BC=E3=83=90=E3=83=BC=E3=81=AE=E6=8A=95=E7=A8=BF=E3=81=8C?= =?UTF-8?q?=E5=8F=82=E7=85=A7=E5=BC=95=E7=94=A8=E3=81=A7=E3=81=8D=E3=81=AA?= =?UTF-8?q?=E3=81=84=E5=95=8F=E9=A1=8C=E3=83=BB=E5=8F=82=E7=85=A7=E5=BC=95?= =?UTF-8?q?=E7=94=A8=E6=99=82=E3=80=81=E3=83=95=E3=82=A7=E3=83=83=E3=83=81?= =?UTF-8?q?=E3=82=92=E5=9F=BA=E6=9C=AC=E3=81=97=E3=81=AA=E3=81=84=E3=82=88?= =?UTF-8?q?=E3=81=86=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/lib/activitypub/activity/create.rb | 2 +- app/lib/activitypub/tag_manager.rb | 6 +- app/services/process_references_service.rb | 13 +++- app/services/resolve_url_service.rb | 9 ++- .../20231214225249_index_to_statuses_url.rb | 13 ++++ db/schema.rb | 3 +- .../process_references_service_spec.rb | 70 +++++++++++++++++++ 7 files changed, 105 insertions(+), 11 deletions(-) create mode 100644 db/migrate/20231214225249_index_to_statuses_url.rb diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index 204899fa39..09b064c9b2 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -505,7 +505,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity url = quote if url.present? - ResolveURLService.new.call(url, on_behalf_of: @account, local_only: true).present? + ActivityPub::TagManager.instance.uri_to_resource(url, Status)&.local? else false end diff --git a/app/lib/activitypub/tag_manager.rb b/app/lib/activitypub/tag_manager.rb index ef3bb8310d..2754299378 100644 --- a/app/lib/activitypub/tag_manager.rb +++ b/app/lib/activitypub/tag_manager.rb @@ -208,7 +208,7 @@ class ActivityPub::TagManager uri_to_resource(uri, Account) end - def uri_to_resource(uri, klass) + def uri_to_resource(uri, klass, url: false) return if uri.nil? if local_uri?(uri) @@ -221,7 +221,9 @@ class ActivityPub::TagManager elsif OStatus::TagManager.instance.local_id?(uri) klass.find_by(id: OStatus::TagManager.instance.unique_tag_to_local_id(uri, klass.to_s)) else - klass.find_by(uri: uri.split('#').first) + resource = klass.find_by(uri: uri.split('#').first) + resource ||= klass.where('uri != url').find_by(url: uri.split('#').first) if url + resource end rescue ActiveRecord::RecordNotFound nil diff --git a/app/services/process_references_service.rb b/app/services/process_references_service.rb index 40405c6ee7..f18a0dc76e 100644 --- a/app/services/process_references_service.rb +++ b/app/services/process_references_service.rb @@ -108,7 +108,16 @@ class ProcessReferencesService < BaseService end def url_to_status(url) - ResolveURLService.new.call(url, on_behalf_of: @status.account, fetch_remote: @fetch_remote && @no_fetch_urls.exclude?(url)) + status = ActivityPub::TagManager.instance.uri_to_resource(url, Status, url: true) + status ||= ResolveURLService.new.call(url, on_behalf_of: @status.account) if @fetch_remote && @no_fetch_urls.exclude?(url) + referrable?(status) ? status : nil + end + + def referrable?(target_status) + return false if target_status.nil? + return @referrable if defined?(@referrable) + + @referrable = StatusPolicy.new(@status.account, target_status).show? end def quote_status_ids @@ -116,7 +125,7 @@ class ProcessReferencesService < BaseService end def quotable?(target_status) - target_status.account.allow_quote? && (!@status.local? || StatusPolicy.new(@status.account, target_status).quote?) + target_status.account.allow_quote? && StatusPolicy.new(@status.account, target_status).quote? end def add_references diff --git a/app/services/resolve_url_service.rb b/app/services/resolve_url_service.rb index 1194afc368..19a94e77ad 100644 --- a/app/services/resolve_url_service.rb +++ b/app/services/resolve_url_service.rb @@ -6,16 +6,15 @@ class ResolveURLService < BaseService USERNAME_STATUS_RE = %r{/@(?#{Account::USERNAME_RE})/(?[0-9]+)\Z} - def call(url, on_behalf_of: nil, fetch_remote: true, local_only: false) + def call(url, on_behalf_of: nil) @url = url @on_behalf_of = on_behalf_of - @fetch_remote = fetch_remote if local_url? process_local_url - elsif !local_only && fetch_remote && !fetched_resource.nil? + elsif !fetched_resource.nil? process_url - elsif !local_only + else process_url_from_db end end @@ -38,7 +37,7 @@ class ResolveURLService < BaseService return account unless account.nil? end - return unless @on_behalf_of.present? && (!@fetch_remote || [401, 403, 404].include?(fetch_resource_service.response_code)) + return unless @on_behalf_of.present? && [401, 403, 404].include?(fetch_resource_service.response_code) # It may happen that the resource is a private toot, and thus not fetchable, # but we can return the toot if we already know about it. diff --git a/db/migrate/20231214225249_index_to_statuses_url.rb b/db/migrate/20231214225249_index_to_statuses_url.rb new file mode 100644 index 0000000000..ab3eff41ab --- /dev/null +++ b/db/migrate/20231214225249_index_to_statuses_url.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class IndexToStatusesURL < ActiveRecord::Migration[7.1] + disable_ddl_transaction! + + def up + add_index :statuses, :url, name: :index_statuses_on_url, algorithm: :concurrently, opclass: :text_pattern_ops, where: 'url IS NOT NULL AND url <> uri' + end + + def down + remove_index :statuses, name: :index_statuses_on_url + end +end diff --git a/db/schema.rb b/db/schema.rb index 62e7ed98fb..3fae126a34 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2023_12_12_225737) do +ActiveRecord::Schema[7.1].define(version: 2023_12_14_225249) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -1242,6 +1242,7 @@ ActiveRecord::Schema[7.1].define(version: 2023_12_12_225737) do t.index ["quote_of_id", "account_id"], name: "index_statuses_on_quote_of_id_and_account_id" t.index ["reblog_of_id", "account_id"], name: "index_statuses_on_reblog_of_id_and_account_id" t.index ["uri"], name: "index_statuses_on_uri", unique: true, opclass: :text_pattern_ops, where: "(uri IS NOT NULL)" + t.index ["url"], name: "index_statuses_on_url", opclass: :text_pattern_ops, where: "((url IS NOT NULL) AND ((url)::text <> (uri)::text))" end create_table "statuses_tags", primary_key: ["tag_id", "status_id"], force: :cascade do |t| diff --git a/spec/services/process_references_service_spec.rb b/spec/services/process_references_service_spec.rb index 37818b2d45..b4c063788a 100644 --- a/spec/services/process_references_service_spec.rb +++ b/spec/services/process_references_service_spec.rb @@ -241,6 +241,76 @@ RSpec.describe ProcessReferencesService, type: :service do end end end + + context 'when already fetched remote post' do + let(:account) { Fabricate(:account, followers_url: 'http://example.com/followers', domain: 'example.com', uri: 'https://example.com/actor') } + let!(:remote_status) { Fabricate(:status, account: account, uri: 'https://example.com/test_post', url: 'https://example.com/test_post', text: 'Lorem ipsum') } + let(:object_json) do + { + id: 'https://example.com/test_post', + to: ActivityPub::TagManager::COLLECTIONS[:public], + '@context': ActivityPub::TagManager::CONTEXT, + type: 'Note', + actor: account.uri, + attributedTo: account.uri, + content: 'Lorem ipsum', + } + end + let(:text) { 'BT:https://example.com/test_post' } + + shared_examples 'reference once' do |uri, url| + it 'reference it' do + expect(subject.size).to eq 1 + expect(subject[0][1]).to eq 'BT' + + status = Status.find_by(id: subject[0][0]) + expect(status).to_not be_nil + expect(status.id).to eq remote_status.id + expect(status.uri).to eq uri + expect(status.url).to eq url + end + end + + before do + stub_request(:get, 'https://example.com/test_post').to_return(status: 200, body: Oj.dump(object_json), headers: { 'Content-Type' => 'application/activity+json' }) + end + + it_behaves_like 'reference once', 'https://example.com/test_post', 'https://example.com/test_post' + + context 'when uri and url is difference and url is not accessable' do + let(:remote_status) { Fabricate(:status, account: account, uri: 'https://example.com/test_post', url: 'https://example.com/test_post_ohagi', text: 'Lorem ipsum') } + let(:text) { 'BT:https://example.com/test_post_ohagi' } + let(:object_json) do + { + id: 'https://example.com/test_post', + url: 'https://example.com/test_post_ohagi', + to: ActivityPub::TagManager::COLLECTIONS[:public], + '@context': ActivityPub::TagManager::CONTEXT, + type: 'Note', + actor: account.uri, + attributedTo: account.uri, + content: 'Lorem ipsum', + } + end + + before do + stub_request(:get, 'https://example.com/test_post_ohagi').to_return(status: 404) + end + + it_behaves_like 'reference once', 'https://example.com/test_post', 'https://example.com/test_post_ohagi' + + it 'do not request to uri' do + subject + expect(a_request(:get, 'https://example.com/test_post_ohagi')).to_not have_been_made + end + + context 'when url and uri is specified at the same time' do + let(:text) { 'BT:https://example.com/test_post_ohagi BT:https://example.com/test_post' } + + it_behaves_like 'reference once', 'https://example.com/test_post', 'https://example.com/test_post_ohagi' + end + end + end end describe 'editing new status' do From 86d4b95be90ed84f8ddbf73739525d8183e8a19d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?KMY=EF=BC=88=E9=9B=AA=E3=81=82=E3=81=99=E3=81=8B=EF=BC=89?= Date: Fri, 15 Dec 2023 09:41:22 +0900 Subject: [PATCH 21/22] Merge pull request from GHSA-qg32-3vrh-w6mw --- app/controllers/concerns/cache_concern.rb | 10 ++++++++ app/controllers/statuses_controller.rb | 31 ++++++++++++++++++++--- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/app/controllers/concerns/cache_concern.rb b/app/controllers/concerns/cache_concern.rb index 2dfe5e263a..6beecc9791 100644 --- a/app/controllers/concerns/cache_concern.rb +++ b/app/controllers/concerns/cache_concern.rb @@ -172,6 +172,16 @@ module CacheConcern def render_with_cache(**options) raise ArgumentError, 'Only JSON render calls are supported' unless options.key?(:json) || block_given? + if options.delete(:cancel_cache) + if block_given? + options[:json] = yield + elsif options[:json].is_a?(Symbol) + options[:json] = send(options[:json]) + end + + return render(options) + end + key = options.delete(:key) || [[params[:controller], params[:action]].join('/'), options[:json].respond_to?(:cache_key) ? options[:json].cache_key : nil, options[:fields].nil? ? nil : options[:fields].join(',')].compact.join(':') expires_in = options.delete(:expires_in) || 3.minutes body = Rails.cache.read(key, raw: true) diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb index 7d18aef132..f5397c3b82 100644 --- a/app/controllers/statuses_controller.rb +++ b/app/controllers/statuses_controller.rb @@ -29,15 +29,15 @@ class StatusesController < ApplicationController end format.json do - expires_in 3.minutes, public: true if @status.distributable? && public_fetch_mode? - render_with_cache json: @status, content_type: 'application/activity+json', serializer: ActivityPub::NoteSerializer, adapter: ActivityPub::Adapter + expires_in 3.minutes, public: true if @status.distributable? && public_fetch_mode? && !misskey_software? + render_with_cache json: @status, content_type: 'application/activity+json', serializer: status_activity_serializer, adapter: ActivityPub::Adapter, cancel_cache: misskey_software? end end end def activity - expires_in 3.minutes, public: @status.distributable? && public_fetch_mode? - render_with_cache json: ActivityPub::ActivityPresenter.from_status(@status), content_type: 'application/activity+json', serializer: ActivityPub::ActivitySerializer, adapter: ActivityPub::Adapter + expires_in 3.minutes, public: @status.distributable? && public_fetch_mode? && !misskey_software? + render_with_cache json: ActivityPub::ActivityPresenter.from_status(@status, for_misskey: misskey_software?), content_type: 'application/activity+json', serializer: ActivityPub::ActivitySerializer, adapter: ActivityPub::Adapter, cancel_cache: misskey_software? end def embed @@ -71,6 +71,29 @@ class StatusesController < ApplicationController not_found end + def misskey_software? + return @misskey_software if defined?(@misskey_software) + + @misskey_software = false + + return false if !@status.local? || signed_request_account&.domain.blank? + + info = InstanceInfo.find_by(domain: signed_request_account.domain) + return false if info.nil? + + @misskey_software = %w(misskey calckey cherrypick sharkey).include?(info.software) && + ((@status.public_unlisted_visibility? && @status.account.user&.setting_reject_public_unlisted_subscription) || + (@status.unlisted_visibility? && @status.account.user&.setting_reject_unlisted_subscription)) + end + + def status_activity_serializer + if misskey_software? + ActivityPub::NoteForMisskeySerializer + else + ActivityPub::NoteSerializer + end + end + def redirect_to_original redirect_to(ActivityPub::TagManager.instance.url_for(@status.reblog), allow_other_host: true) if @status.reblog? end From 91857a6fa39a58621db04c52047b1be97747c185 Mon Sep 17 00:00:00 2001 From: KMY Date: Mon, 11 Dec 2023 14:04:07 +0900 Subject: [PATCH 22/22] Bump version to 9.4 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index c0f90aad19..e6b982e532 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -9,7 +9,7 @@ module Mastodon end def kmyblue_minor - 3 + 4 end def kmyblue_flag