Compare commits

...
Sign in to create a new pull request.

11 commits

11 changed files with 119 additions and 30 deletions

View file

@ -2,6 +2,48 @@
All notable changes to this project will be documented in this file.
## [4.3.2] - 2024-12-03
### Added
- Add `tootctl feeds vacuum` (#33065 by @ClearlyClaire)
- Add error message when user tries to follow their own account (#31910 by @lenikadali)
- Add client_secret_expires_at to OAuth Applications (#30317 by @ThisIsMissEm)
### Changed
- Change design of Content Warnings and filters (#32543 by @ClearlyClaire)
### Fixed
- Fix processing incoming post edits with mentions to unresolvable accounts (#33129 by @ClearlyClaire)
- Fix error when including multiple instances of `embed.js` (#33107 by @YKWeyer)
- Fix inactive users' timelines being backfilled on follow and unsuspend (#33094 by @ClearlyClaire)
- Fix direct inbox delivery pushing posts into inactive followers' timelines (#33067 by @ClearlyClaire)
- Fix `TagFollow` records not being correctly handled in account operations (#33063 by @ClearlyClaire)
- Fix pushing hashtag-followed posts to feeds of inactive users (#33018 by @Gargron)
- Fix duplicate notifications in notification groups when using slow mode (#33014 by @ClearlyClaire)
- Fix posts made in the future being allowed to trend (#32996 by @ClearlyClaire)
- Fix uploading higher-than-wide GIF profile picture with libvips enabled (#32911 by @ClearlyClaire)
- Fix domain attribution field having autocorrect and autocapitalize enabled (#32903 by @ClearlyClaire)
- Fix titles being escaped twice (#32889 by @ClearlyClaire)
- Fix list creation limit check (#32869 by @ClearlyClaire)
- Fix error in `tootctl email_domain_blocks` when supplying `--with-dns-records` (#32863 by @mjankowski)
- Fix `min_id` and `max_id` causing error in search API (#32857 by @Gargron)
- Fix inefficiencies when processing removal of posts that use featured tags (#32787 by @ClearlyClaire)
- Fix alt-text pop-in not using the translated description (#32766 by @ClearlyClaire)
- Fix preview cards with long titles erroneously causing layout changes (#32678 by @ClearlyClaire)
- Fix embed modal layout on mobile (#32641 by @DismalShadowX)
- Fix and improve batch attachment deletion handling when using OpenStack Swift (#32637 by @hugogameiro)
- Fix blocks not being applied on link timeline (#32625 by @tribela)
- Fix follow counters being incorrectly changed (#32622 by @oneiros)
- Fix 'unknown' media attachment type rendering (#32613 and #32713 by @ThisIsMissEm and @renatolond)
- Fix tl language native name (#32606 by @seav)
### Security
- Update dependencies
## [4.3.1] - 2024-10-21
### Added

View file

@ -60,19 +60,13 @@ class ActivityPub::Activity::Follow < ActivityPub::Activity
already_accepted = friend.accepted?
friend.update!(passive_state: :pending, active_state: :idle, passive_follow_activity_id: @json['id'])
else
@friend = FriendDomain.new(domain: @account.domain, passive_state: :pending, passive_follow_activity_id: @json['id'])
@friend.inbox_url = @json['inboxUrl'].presence || @friend.default_inbox_url
@friend.save!
@friend = FriendDomain.create!(domain: @account.domain, passive_state: :pending, passive_follow_activity_id: @json['id'], inbox_url: @account.preferred_inbox_url)
end
if already_accepted || Setting.unlocked_friend
friend.accept!
friend.accept! if already_accepted || Setting.unlocked_friend
# Notify for admin even if unlocked
notify_staff_about_pending_friend_server! unless already_accepted
else
notify_staff_about_pending_friend_server!
end
# Notify for admin
notify_staff_about_pending_friend_server! unless already_accepted
end
def friend

View file

@ -125,6 +125,8 @@ class FeedManager
# @param [Account] into_account
# @return [void]
def merge_into_home(from_account, into_account)
return unless into_account.user&.signed_in_recently?
timeline_key = key(:home, into_account.id)
aggregate = into_account.user&.aggregates_reblogs?
query = from_account.statuses.list_eligible_visibility.includes(:preloadable_poll, :media_attachments, reblog: :account).limit(FeedManager::MAX_ITEMS / 4)
@ -151,6 +153,8 @@ class FeedManager
# @param [List] list
# @return [void]
def merge_into_list(from_account, list)
return unless list.account.user&.signed_in_recently?
timeline_key = key(:list, list.id)
aggregate = list.account.user&.aggregates_reblogs?
query = from_account.statuses.list_eligible_visibility.includes(:preloadable_poll, :media_attachments, reblog: :account).limit(FeedManager::MAX_ITEMS / 4)

View file

@ -116,6 +116,7 @@ class FriendDomain < ApplicationRecord
object: ActivityPub::TagManager::COLLECTIONS[:public],
# Cannot use inbox_url method because this model also has inbox_url column
# This is deprecated property. Newer version's kmyblue will ignore it.
inboxUrl: "https://#{Rails.configuration.x.web_domain}/inbox",
}
end

View file

@ -261,6 +261,7 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
def update_mentions!
previous_mentions = @status.active_mentions.includes(:account).to_a
current_mentions = []
unresolved_mentions = []
@raw_mentions.each do |href|
next if href.blank?
@ -274,6 +275,12 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
mention ||= account.mentions.new(status: @status)
current_mentions << mention
rescue Mastodon::UnexpectedResponseError, *Mastodon::HTTP_CONNECTION_ERRORS
# Since previous mentions are about already-known accounts,
# they don't try to resolve again and won't fall into this case.
# In other words, this failure case is only for new mentions and won't
# affect `removed_mentions` so they can safely be retried asynchronously
unresolved_mentions << href
end
current_mentions.each do |mention|
@ -286,6 +293,11 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
removed_mentions = previous_mentions - current_mentions
Mention.where(id: removed_mentions.map(&:id)).update_all(silent: true) unless removed_mentions.empty?
# Queue unresolved mentions for later
unresolved_mentions.uniq.each do |uri|
MentionResolveWorker.perform_in(rand(30...600).seconds, @status.id, uri, { 'request_id' => @request_id })
end
end
def update_emojis!

View file

@ -59,7 +59,7 @@ services:
web:
# You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes
build: .
image: kmyblue:16.0-dev
image: kmyblue:16.1
restart: always
env_file: .env.production
command: bundle exec puma -C config/puma.rb
@ -83,7 +83,7 @@ services:
build:
dockerfile: ./streaming/Dockerfile
context: .
image: kmyblue-streaming:16.0-dev
image: kmyblue-streaming:16.1
restart: always
env_file: .env.production
command: node ./streaming/index.js
@ -101,7 +101,7 @@ services:
sidekiq:
build: .
image: kmyblue:16.0-dev
image: kmyblue:16.1
restart: always
env_file: .env.production
command: bundle exec sidekiq

View file

@ -5,6 +5,7 @@ require_relative 'base'
module Mastodon::CLI
class Feeds < Base
include Redisable
include DatabaseHelper
option :all, type: :boolean, default: false
option :concurrency, type: :numeric, default: 5, aliases: [:c]
@ -59,6 +60,38 @@ module Mastodon::CLI
say('OK', :green)
end
desc 'vacuum', 'Remove home feeds of inactive users from Redis'
long_desc <<-LONG_DESC
Running this task should not be needed in most cases, as Mastodon will
automatically clean up feeds from inactive accounts every day.
However, this task is more aggressive in order to clean up feeds that
may have been missed because of bugs or database mishaps.
LONG_DESC
def vacuum
with_read_replica do
say('Deleting orphaned home feeds…')
redis.scan_each(match: 'feed:home:*').each_slice(1000) do |keys|
ids = keys.map { |key| key.split(':')[2] }.compact_blank
known_ids = User.confirmed.signed_in_recently.where(account_id: ids).pluck(:account_id)
keys_to_delete = keys.filter { |key| known_ids.exclude?(key.split(':')[2]&.to_i) }
redis.del(keys_to_delete)
end
say('Deleting orphaned list feeds…')
redis.scan_each(match: 'feed:list:*').each_slice(1000) do |keys|
ids = keys.map { |key| key.split(':')[2] }.compact_blank
known_ids = List.where(account_id: User.confirmed.signed_in_recently.select(:account_id)).where(id: ids).pluck(:id)
keys_to_delete = keys.filter { |key| known_ids.exclude?(key.split(':')[2]&.to_i) }
redis.del(keys_to_delete)
end
end
end
private
def active_user_accounts

View file

@ -13,13 +13,13 @@ module Mastodon
end
def kmyblue_minor
0
1
end
def kmyblue_flag
# 'LTS'
'dev'
# nil
# 'dev'
nil
end
def major

View file

@ -1,8 +1,5 @@
// @ts-check
const allowedPrefixes = (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT' && document.currentScript.dataset.allowedPrefixes) ? document.currentScript.dataset.allowedPrefixes.split(' ') : [];
(function () {
(function (allowedPrefixes) {
'use strict';
/**
@ -127,4 +124,4 @@ const allowedPrefixes = (document.currentScript && document.currentScript.tagNam
container.appendChild(iframe);
});
});
})();
})((document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT' && document.currentScript.dataset.allowedPrefixes) ? document.currentScript.dataset.allowedPrefixes.split(' ') : []);

View file

@ -380,11 +380,10 @@ RSpec.describe ActivityPub::Activity::Follow do
context 'when given a friend server' do
subject { described_class.new(json, sender) }
let(:sender) { Fabricate(:account, domain: 'abc.com', url: 'https://abc.com/#actor') }
let(:sender) { Fabricate(:account, domain: 'abc.com', url: 'https://abc.com/#actor', shared_inbox_url: 'https://abc.com/shared_inbox') }
let!(:friend) { Fabricate(:friend_domain, domain: 'abc.com', inbox_url: 'https://example.com/inbox', passive_state: :idle) }
let!(:owner_user) { Fabricate(:user, role: UserRole.find_by(name: 'Owner')) }
let!(:patch_user) { Fabricate(:user, role: Fabricate(:user_role, name: 'OhagiOps', permissions: UserRole::FLAGS[:manage_federation])) }
let(:inbox_url) { nil }
let(:json) do
{
@ -393,7 +392,6 @@ RSpec.describe ActivityPub::Activity::Follow do
type: 'Follow',
actor: ActivityPub::TagManager.instance.uri_for(sender),
object: 'https://www.w3.org/ns/activitystreams#Public',
inboxUrl: inbox_url,
}.with_indifferent_access
end
@ -415,25 +413,34 @@ RSpec.describe ActivityPub::Activity::Follow do
expect(friend).to_not be_nil
expect(friend.they_are_pending?).to be true
expect(friend.passive_follow_activity_id).to eq 'foo'
expect(friend.inbox_url).to eq 'https://abc.com/inbox'
expect(friend.inbox_url).to eq 'https://abc.com/shared_inbox'
end
end
context 'when no record and inbox_url is specified' do
let(:inbox_url) { 'https://ohagi.com/inbox' }
context 'when old spec which no record and inbox_url is specified' do
let(:json) do
{
'@context': 'https://www.w3.org/ns/activitystreams',
id: 'foo',
type: 'Follow',
actor: ActivityPub::TagManager.instance.uri_for(sender),
object: 'https://www.w3.org/ns/activitystreams#Public',
inboxUrl: 'https://evil.org/bad_inbox',
}.with_indifferent_access
end
before do
friend.destroy!
end
it 'marks the friend as pending' do
it 'marks the friend as pending but inboxUrl is not working' do
subject.perform
friend = FriendDomain.find_by(domain: 'abc.com')
expect(friend).to_not be_nil
expect(friend.they_are_pending?).to be true
expect(friend.passive_follow_activity_id).to eq 'foo'
expect(friend.inbox_url).to eq 'https://ohagi.com/inbox'
expect(friend.inbox_url).to eq 'https://abc.com/shared_inbox'
end
end

View file

@ -21,7 +21,6 @@ RSpec.describe FriendDomain do
type: 'Follow',
actor: 'https://cb6e6126.ngrok.io/actor',
object: 'https://www.w3.org/ns/activitystreams#Public',
inboxUrl: 'https://cb6e6126.ngrok.io/inbox',
}))).to have_been_made.once
end
end