mirror of
https://github.com/glitch-soc/mastodon
synced 2025-04-24 15:24:51 +00:00
Merge commit 'c43508b3e0b05c5e739d726bad53f1eef89e9376' into glitch-soc/merge-upstream
Conflicts: - `app/lib/content_security_policy.rb`: Upstream added support for `EXTRA_MEDIA_HOSTS` which is very similar to glitch-soc's `EXTRA_DATA_HOST`. Deprecate `EXTRA_DATA_HOST` FOR `EXTRA_MEDIA_HOST`.
This commit is contained in:
commit
449dc30739
13 changed files with 196 additions and 40 deletions
|
@ -309,6 +309,9 @@ MAX_POLL_OPTION_CHARS=100
|
||||||
# HCAPTCHA_SECRET_KEY=
|
# HCAPTCHA_SECRET_KEY=
|
||||||
# HCAPTCHA_SITE_KEY=
|
# HCAPTCHA_SITE_KEY=
|
||||||
|
|
||||||
|
# Optional list of hosts that are allowed to serve media for your instance
|
||||||
|
# EXTRA_MEDIA_HOSTS=https://data.example1.com,https://data.example2.com
|
||||||
|
|
||||||
# IP and session retention
|
# IP and session retention
|
||||||
# -----------------------
|
# -----------------------
|
||||||
# Make sure to modify the scheduling of ip_cleanup_scheduler in config/sidekiq.yml
|
# Make sure to modify the scheduling of ip_cleanup_scheduler in config/sidekiq.yml
|
||||||
|
|
|
@ -81,9 +81,14 @@
|
||||||
"alert.rate_limited.title": "Feur bevennet",
|
"alert.rate_limited.title": "Feur bevennet",
|
||||||
"alert.unexpected.message": "Ur fazi dic'hortozet zo degouezhet.",
|
"alert.unexpected.message": "Ur fazi dic'hortozet zo degouezhet.",
|
||||||
"alert.unexpected.title": "Hopala !",
|
"alert.unexpected.title": "Hopala !",
|
||||||
|
"alt_text_modal.cancel": "Nullañ",
|
||||||
|
"alt_text_modal.change_thumbnail": "Kemmañ ar velvenn",
|
||||||
|
"alt_text_modal.done": "Graet",
|
||||||
"announcement.announcement": "Kemennad",
|
"announcement.announcement": "Kemennad",
|
||||||
"annual_report.summary.followers.followers": "heulier",
|
"annual_report.summary.followers.followers": "heulier",
|
||||||
"annual_report.summary.highlighted_post.possessive": "{name}",
|
"annual_report.summary.highlighted_post.possessive": "{name}",
|
||||||
|
"annual_report.summary.most_used_hashtag.none": "Hini ebet",
|
||||||
|
"annual_report.summary.new_posts.new_posts": "toudoù nevez",
|
||||||
"attachments_list.unprocessed": "(ket meret)",
|
"attachments_list.unprocessed": "(ket meret)",
|
||||||
"audio.hide": "Kuzhat ar c'hleved",
|
"audio.hide": "Kuzhat ar c'hleved",
|
||||||
"block_modal.show_less": "Diskouez nebeutoc'h",
|
"block_modal.show_less": "Diskouez nebeutoc'h",
|
||||||
|
@ -109,9 +114,11 @@
|
||||||
"column.blocks": "Implijer·ezed·ien berzet",
|
"column.blocks": "Implijer·ezed·ien berzet",
|
||||||
"column.bookmarks": "Sinedoù",
|
"column.bookmarks": "Sinedoù",
|
||||||
"column.community": "Red-amzer lec'hel",
|
"column.community": "Red-amzer lec'hel",
|
||||||
|
"column.create_list": "Krouiñ ul listenn",
|
||||||
"column.direct": "Menegoù prevez",
|
"column.direct": "Menegoù prevez",
|
||||||
"column.directory": "Mont a-dreuz ar profiloù",
|
"column.directory": "Mont a-dreuz ar profiloù",
|
||||||
"column.domain_blocks": "Domani berzet",
|
"column.domain_blocks": "Domani berzet",
|
||||||
|
"column.edit_list": "Kemmañ al listenn",
|
||||||
"column.favourites": "Muiañ-karet",
|
"column.favourites": "Muiañ-karet",
|
||||||
"column.firehose": "Redoù war-eeun",
|
"column.firehose": "Redoù war-eeun",
|
||||||
"column.follow_requests": "Rekedoù heuliañ",
|
"column.follow_requests": "Rekedoù heuliañ",
|
||||||
|
@ -162,9 +169,12 @@
|
||||||
"confirmations.delete.message": "Ha sur oc'h e fell deoc'h dilemel an toud-mañ ?",
|
"confirmations.delete.message": "Ha sur oc'h e fell deoc'h dilemel an toud-mañ ?",
|
||||||
"confirmations.delete_list.confirm": "Dilemel",
|
"confirmations.delete_list.confirm": "Dilemel",
|
||||||
"confirmations.delete_list.message": "Ha sur eo hoc'h eus c'hoant da zilemel ar roll-mañ da vat ?",
|
"confirmations.delete_list.message": "Ha sur eo hoc'h eus c'hoant da zilemel ar roll-mañ da vat ?",
|
||||||
|
"confirmations.delete_list.title": "Dilemel al listenn?",
|
||||||
"confirmations.discard_edit_media.confirm": "Nac'hañ",
|
"confirmations.discard_edit_media.confirm": "Nac'hañ",
|
||||||
"confirmations.discard_edit_media.message": "Bez ez eus kemmoù n'int ket enrollet e deskrivadur ar media pe ar rakwel, nullañ anezho evelato?",
|
"confirmations.discard_edit_media.message": "Bez ez eus kemmoù n'int ket enrollet e deskrivadur ar media pe ar rakwel, nullañ anezho evelato?",
|
||||||
"confirmations.edit.confirm": "Kemmañ",
|
"confirmations.edit.confirm": "Kemmañ",
|
||||||
|
"confirmations.edit.message": "Kemmañ bremañ a zilamo ar gemennadenn emaoc'h o skrivañ. Sur e oc'h e fell deoc'h kenderc'hel ganti?",
|
||||||
|
"confirmations.follow_to_list.title": "Heuliañ an implijer·ez?",
|
||||||
"confirmations.logout.confirm": "Digevreañ",
|
"confirmations.logout.confirm": "Digevreañ",
|
||||||
"confirmations.logout.message": "Ha sur oc'h e fell deoc'h digevreañ ?",
|
"confirmations.logout.message": "Ha sur oc'h e fell deoc'h digevreañ ?",
|
||||||
"confirmations.mute.confirm": "Kuzhat",
|
"confirmations.mute.confirm": "Kuzhat",
|
||||||
|
@ -266,8 +276,10 @@
|
||||||
"footer.privacy_policy": "Reolennoù prevezded",
|
"footer.privacy_policy": "Reolennoù prevezded",
|
||||||
"footer.source_code": "Gwelet ar c'hod mammenn",
|
"footer.source_code": "Gwelet ar c'hod mammenn",
|
||||||
"footer.status": "Statud",
|
"footer.status": "Statud",
|
||||||
|
"footer.terms_of_service": "Divizoù implijout hollek",
|
||||||
"generic.saved": "Enrollet",
|
"generic.saved": "Enrollet",
|
||||||
"getting_started.heading": "Loc'hañ",
|
"getting_started.heading": "Loc'hañ",
|
||||||
|
"hashtag.admin_moderation": "Digeriñ an etrefas evezhiañ evit #{name}",
|
||||||
"hashtag.column_header.tag_mode.all": "ha(g) {additional}",
|
"hashtag.column_header.tag_mode.all": "ha(g) {additional}",
|
||||||
"hashtag.column_header.tag_mode.any": "pe {additional}",
|
"hashtag.column_header.tag_mode.any": "pe {additional}",
|
||||||
"hashtag.column_header.tag_mode.none": "hep {additional}",
|
"hashtag.column_header.tag_mode.none": "hep {additional}",
|
||||||
|
@ -337,8 +349,14 @@
|
||||||
"limited_account_hint.action": "Diskouez an aelad memes tra",
|
"limited_account_hint.action": "Diskouez an aelad memes tra",
|
||||||
"limited_account_hint.title": "Kuzhet eo bet ar profil-mañ gant an evezhierien eus {domain}.",
|
"limited_account_hint.title": "Kuzhet eo bet ar profil-mañ gant an evezhierien eus {domain}.",
|
||||||
"link_preview.author": "Gant {name}",
|
"link_preview.author": "Gant {name}",
|
||||||
|
"lists.add_member": "Ouzhpennañ",
|
||||||
|
"lists.add_to_list": "Ouzhpennañ d'al listenn",
|
||||||
|
"lists.create": "Krouiñ",
|
||||||
|
"lists.create_list": "Krouiñ ul listenn",
|
||||||
"lists.delete": "Dilemel al listenn",
|
"lists.delete": "Dilemel al listenn",
|
||||||
|
"lists.done": "Graet",
|
||||||
"lists.edit": "Kemmañ al listenn",
|
"lists.edit": "Kemmañ al listenn",
|
||||||
|
"lists.list_name": "Anv al listenn",
|
||||||
"lists.replies_policy.followed": "Pep implijer.ez heuliet",
|
"lists.replies_policy.followed": "Pep implijer.ez heuliet",
|
||||||
"lists.replies_policy.list": "Izili ar roll",
|
"lists.replies_policy.list": "Izili ar roll",
|
||||||
"lists.replies_policy.none": "Den ebet",
|
"lists.replies_policy.none": "Den ebet",
|
||||||
|
@ -373,11 +391,17 @@
|
||||||
"notification.follow": "heuliañ a ra {name} ac'hanoc'h",
|
"notification.follow": "heuliañ a ra {name} ac'hanoc'h",
|
||||||
"notification.follow.name_and_others": "{name} <a>{count, plural, one {hag # den all} two {ha # zen all} few {ha # den all} many {ha # den all} other {ha # den all}}</a> zo o heuliañ ac'hanoc'h",
|
"notification.follow.name_and_others": "{name} <a>{count, plural, one {hag # den all} two {ha # zen all} few {ha # den all} many {ha # den all} other {ha # den all}}</a> zo o heuliañ ac'hanoc'h",
|
||||||
"notification.follow_request": "Gant {name} eo bet goulennet ho heuliañ",
|
"notification.follow_request": "Gant {name} eo bet goulennet ho heuliañ",
|
||||||
|
"notification.label.reply": "Respont",
|
||||||
"notification.moderation-warning.learn_more": "Gouzout hiroc'h",
|
"notification.moderation-warning.learn_more": "Gouzout hiroc'h",
|
||||||
"notification.own_poll": "Echu eo ho sontadeg",
|
"notification.own_poll": "Echu eo ho sontadeg",
|
||||||
"notification.reblog": "Gant {name} eo bet skignet ho toud",
|
"notification.reblog": "Gant {name} eo bet skignet ho toud",
|
||||||
|
"notification.relationships_severance_event.learn_more": "Gouzout hiroc'h",
|
||||||
"notification.status": "Emañ {name} o paouez toudañ",
|
"notification.status": "Emañ {name} o paouez toudañ",
|
||||||
"notification.update": "Gant {name} ez eus bet kemmet un toud",
|
"notification.update": "Gant {name} ez eus bet kemmet un toud",
|
||||||
|
"notification_requests.accept": "Asantiñ",
|
||||||
|
"notification_requests.dismiss": "Diverkañ",
|
||||||
|
"notification_requests.edit_selection": "Kemmañ",
|
||||||
|
"notification_requests.exit_selection": "Graet",
|
||||||
"notifications.clear": "Skarzhañ ar c'hemennoù",
|
"notifications.clear": "Skarzhañ ar c'hemennoù",
|
||||||
"notifications.clear_confirmation": "Ha sur oc'h e fell deoc'h skarzhañ ho holl kemennoù ?",
|
"notifications.clear_confirmation": "Ha sur oc'h e fell deoc'h skarzhañ ho holl kemennoù ?",
|
||||||
"notifications.column_settings.admin.report": "Disklêriadurioù nevez :",
|
"notifications.column_settings.admin.report": "Disklêriadurioù nevez :",
|
||||||
|
@ -410,6 +434,10 @@
|
||||||
"notifications.permission_denied": "Kemennoù war ar burev n'int ket hegerz rak pedadenn aotren ar merdeer a zo bet nullet araok",
|
"notifications.permission_denied": "Kemennoù war ar burev n'int ket hegerz rak pedadenn aotren ar merdeer a zo bet nullet araok",
|
||||||
"notifications.permission_denied_alert": "Kemennoù wa ar burev na c'hellont ket bezañ lezelet, rak aotre ar merdeer a zo bet nac'het a-raok",
|
"notifications.permission_denied_alert": "Kemennoù wa ar burev na c'hellont ket bezañ lezelet, rak aotre ar merdeer a zo bet nac'het a-raok",
|
||||||
"notifications.permission_required": "Kemennoù war ar burev n'int ket hegerz abalamour d'an aotre rekis n'eo ket bet roet.",
|
"notifications.permission_required": "Kemennoù war ar burev n'int ket hegerz abalamour d'an aotre rekis n'eo ket bet roet.",
|
||||||
|
"notifications.policy.accept": "Asantiñ",
|
||||||
|
"notifications.policy.accept_hint": "Diskouez er c’hemennoù",
|
||||||
|
"notifications.policy.drop": "Tremen e-bioù",
|
||||||
|
"notifications.policy.filter": "Silañ",
|
||||||
"notifications.policy.filter_new_accounts_title": "Kontoù nevez",
|
"notifications.policy.filter_new_accounts_title": "Kontoù nevez",
|
||||||
"notifications_permission_banner.enable": "Lezel kemennoù war ar burev",
|
"notifications_permission_banner.enable": "Lezel kemennoù war ar burev",
|
||||||
"notifications_permission_banner.how_to_control": "Evit reseviñ kemennoù pa ne vez ket digoret Mastodon, lezelit kemennoù war ar burev. Gallout a rit kontrollañ peseurt eskemmoù a c'henel kemennoù war ar burev gant ar {icon} nozelenn a-us kentre ma'z int lezelet.",
|
"notifications_permission_banner.how_to_control": "Evit reseviñ kemennoù pa ne vez ket digoret Mastodon, lezelit kemennoù war ar burev. Gallout a rit kontrollañ peseurt eskemmoù a c'henel kemennoù war ar burev gant ar {icon} nozelenn a-us kentre ma'z int lezelet.",
|
||||||
|
@ -515,6 +543,7 @@
|
||||||
"search_results.accounts": "Profiloù",
|
"search_results.accounts": "Profiloù",
|
||||||
"search_results.all": "Pep tra",
|
"search_results.all": "Pep tra",
|
||||||
"search_results.hashtags": "Hashtagoù",
|
"search_results.hashtags": "Hashtagoù",
|
||||||
|
"search_results.no_results": "Disoc'h ebet.",
|
||||||
"search_results.see_all": "Gwelet pep tra",
|
"search_results.see_all": "Gwelet pep tra",
|
||||||
"search_results.statuses": "Toudoù",
|
"search_results.statuses": "Toudoù",
|
||||||
"server_banner.active_users": "implijerien·ezed oberiant",
|
"server_banner.active_users": "implijerien·ezed oberiant",
|
||||||
|
@ -579,6 +608,7 @@
|
||||||
"subscribed_languages.target": "Cheñch ar yezhoù koumanantet evit {target}",
|
"subscribed_languages.target": "Cheñch ar yezhoù koumanantet evit {target}",
|
||||||
"tabs_bar.home": "Degemer",
|
"tabs_bar.home": "Degemer",
|
||||||
"tabs_bar.notifications": "Kemennoù",
|
"tabs_bar.notifications": "Kemennoù",
|
||||||
|
"terms_of_service.title": "Divizoù implijout",
|
||||||
"time_remaining.days": "{number, plural,one {# devezh} other {# a zevezh}} a chom",
|
"time_remaining.days": "{number, plural,one {# devezh} other {# a zevezh}} a chom",
|
||||||
"time_remaining.hours": "{number, plural, one {# eurvezh} other{# eurvezh}} a chom",
|
"time_remaining.hours": "{number, plural, one {# eurvezh} other{# eurvezh}} a chom",
|
||||||
"time_remaining.minutes": "{number, plural, one {# munut} other{# a vunut}} a chom",
|
"time_remaining.minutes": "{number, plural, one {# munut} other{# a vunut}} a chom",
|
||||||
|
|
|
@ -562,6 +562,7 @@
|
||||||
"notification.favourite": "{name} märkis su postituse lemmikuks",
|
"notification.favourite": "{name} märkis su postituse lemmikuks",
|
||||||
"notification.favourite.name_and_others_with_link": "{name} ja <a>{count, plural, one {# veel} other {# teist}}</a> märkis su postituse lemmikuks",
|
"notification.favourite.name_and_others_with_link": "{name} ja <a>{count, plural, one {# veel} other {# teist}}</a> märkis su postituse lemmikuks",
|
||||||
"notification.favourite_pm": "{name} märkis sinu privaatse mainimise lemmikuks",
|
"notification.favourite_pm": "{name} märkis sinu privaatse mainimise lemmikuks",
|
||||||
|
"notification.favourite_pm.name_and_others_with_link": "{name} ja <a>{count, plural, one {# veel} other {# veel}}</a> märkisid su privaatse mainimise lemmikuks",
|
||||||
"notification.follow": "{name} alustas su jälgimist",
|
"notification.follow": "{name} alustas su jälgimist",
|
||||||
"notification.follow.name_and_others": "{name} ja veel {count, plural, one {# kasutaja} other {# kasutajat}} hakkas sind jälgima",
|
"notification.follow.name_and_others": "{name} ja veel {count, plural, one {# kasutaja} other {# kasutajat}} hakkas sind jälgima",
|
||||||
"notification.follow_request": "{name} soovib sind jälgida",
|
"notification.follow_request": "{name} soovib sind jälgida",
|
||||||
|
@ -696,6 +697,7 @@
|
||||||
"poll_button.remove_poll": "Eemalda küsitlus",
|
"poll_button.remove_poll": "Eemalda küsitlus",
|
||||||
"privacy.change": "Muuda postituse nähtavust",
|
"privacy.change": "Muuda postituse nähtavust",
|
||||||
"privacy.direct.long": "Kõik postituses mainitud",
|
"privacy.direct.long": "Kõik postituses mainitud",
|
||||||
|
"privacy.direct.short": "Privaatne mainimine",
|
||||||
"privacy.private.long": "Ainult jälgijad",
|
"privacy.private.long": "Ainult jälgijad",
|
||||||
"privacy.private.short": "Jälgijad",
|
"privacy.private.short": "Jälgijad",
|
||||||
"privacy.public.long": "Nii kasutajad kui mittekasutajad",
|
"privacy.public.long": "Nii kasutajad kui mittekasutajad",
|
||||||
|
|
|
@ -10,7 +10,7 @@ class ContentSecurityPolicy
|
||||||
end
|
end
|
||||||
|
|
||||||
def media_hosts
|
def media_hosts
|
||||||
[assets_host, cdn_host_value, paperclip_root_url].concat(extra_data_hosts).compact
|
[assets_host, cdn_host_value, paperclip_root_url].concat(extra_media_hosts).compact
|
||||||
end
|
end
|
||||||
|
|
||||||
def sso_host
|
def sso_host
|
||||||
|
@ -31,8 +31,17 @@ class ContentSecurityPolicy
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
# TODO: remove after 4.4.0
|
||||||
def extra_data_hosts
|
def extra_data_hosts
|
||||||
ENV.fetch('EXTRA_DATA_HOSTS', '').split('|')
|
return [] unless ENV['EXTRA_DATA_HOSTS']
|
||||||
|
|
||||||
|
ENV.fetch('EXTRA_DATA_HOSTS', '').split('|').tap do |hosts|
|
||||||
|
Rails.logger.warn "EXTRA_DATA_HOSTS is deprecated, use EXTRA_MEDIA_HOSTS=#{hosts.join(',')}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def extra_media_hosts
|
||||||
|
ENV.fetch('EXTRA_MEDIA_HOSTS', '').split(/(?:\s*,\s*|\s+)/).presence || extra_data_hosts
|
||||||
end
|
end
|
||||||
|
|
||||||
def url_from_configured_asset_host
|
def url_from_configured_asset_host
|
||||||
|
|
|
@ -153,7 +153,7 @@ class Account < ApplicationRecord
|
||||||
scope :not_excluded_by_account, ->(account) { where.not(id: account.excluded_from_timeline_account_ids) }
|
scope :not_excluded_by_account, ->(account) { where.not(id: account.excluded_from_timeline_account_ids) }
|
||||||
scope :not_domain_blocked_by_account, ->(account) { where(arel_table[:domain].eq(nil).or(arel_table[:domain].not_in(account.excluded_from_timeline_domains))) }
|
scope :not_domain_blocked_by_account, ->(account) { where(arel_table[:domain].eq(nil).or(arel_table[:domain].not_in(account.excluded_from_timeline_domains))) }
|
||||||
scope :dormant, -> { joins(:account_stat).merge(AccountStat.without_recent_activity) }
|
scope :dormant, -> { joins(:account_stat).merge(AccountStat.without_recent_activity) }
|
||||||
scope :with_username, ->(value) { where arel_table[:username].lower.eq(value.to_s.downcase) }
|
scope :with_username, ->(value) { value.is_a?(Array) ? where(arel_table[:username].lower.in(value.map { |x| x.to_s.downcase })) : where(arel_table[:username].lower.eq(value.to_s.downcase)) }
|
||||||
scope :with_domain, ->(value) { where arel_table[:domain].lower.eq(value&.to_s&.downcase) }
|
scope :with_domain, ->(value) { where arel_table[:domain].lower.eq(value&.to_s&.downcase) }
|
||||||
scope :without_memorial, -> { where(memorial: false) }
|
scope :without_memorial, -> { where(memorial: false) }
|
||||||
scope :duplicate_uris, -> { select(:uri, Arel.star.count).group(:uri).having(Arel.star.count.gt(1)) }
|
scope :duplicate_uris, -> { select(:uri, Arel.star.count).group(:uri).having(Arel.star.count.gt(1)) }
|
||||||
|
|
|
@ -107,6 +107,7 @@ class REST::InstanceSerializer < ActiveModel::Serializer
|
||||||
{
|
{
|
||||||
enabled: registrations_enabled?,
|
enabled: registrations_enabled?,
|
||||||
approval_required: Setting.registrations_mode == 'approved',
|
approval_required: Setting.registrations_mode == 'approved',
|
||||||
|
reason_required: Setting.registrations_mode == 'approved' && Setting.require_invite_text,
|
||||||
message: registrations_enabled? ? nil : registrations_message,
|
message: registrations_enabled? ? nil : registrations_message,
|
||||||
min_age: Setting.min_age.presence,
|
min_age: Setting.min_age.presence,
|
||||||
url: ENV.fetch('SSO_ACCOUNT_SIGN_UP', nil),
|
url: ENV.fetch('SSO_ACCOUNT_SIGN_UP', nil),
|
||||||
|
|
|
@ -4,32 +4,46 @@ class ActivityPub::SynchronizeFollowersService < BaseService
|
||||||
include JsonLdHelper
|
include JsonLdHelper
|
||||||
include Payloadable
|
include Payloadable
|
||||||
|
|
||||||
|
MAX_COLLECTION_PAGES = 10
|
||||||
|
|
||||||
def call(account, partial_collection_url)
|
def call(account, partial_collection_url)
|
||||||
@account = account
|
@account = account
|
||||||
|
@expected_followers_ids = []
|
||||||
|
|
||||||
items = collection_items(partial_collection_url)
|
return unless process_collection!(partial_collection_url)
|
||||||
return if items.nil?
|
|
||||||
|
|
||||||
# There could be unresolved accounts (hence the call to .compact) but this
|
|
||||||
# should never happen in practice, since in almost all cases we keep an
|
|
||||||
# Account record, and should we not do that, we should have sent a Delete.
|
|
||||||
# In any case there is not much we can do if that occurs.
|
|
||||||
@expected_followers = items.filter_map { |uri| ActivityPub::TagManager.instance.uri_to_resource(uri, Account) }
|
|
||||||
|
|
||||||
remove_unexpected_local_followers!
|
remove_unexpected_local_followers!
|
||||||
handle_unexpected_outgoing_follows!
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def process_page!(items)
|
||||||
|
page_expected_followers = extract_local_followers(items)
|
||||||
|
@expected_followers_ids.concat(page_expected_followers.pluck(:id))
|
||||||
|
|
||||||
|
handle_unexpected_outgoing_follows!(page_expected_followers)
|
||||||
|
end
|
||||||
|
|
||||||
|
def extract_local_followers(items)
|
||||||
|
# There could be unresolved accounts (hence the call to .filter_map) but this
|
||||||
|
# should never happen in practice, since in almost all cases we keep an
|
||||||
|
# Account record, and should we not do that, we should have sent a Delete.
|
||||||
|
# In any case there is not much we can do if that occurs.
|
||||||
|
|
||||||
|
# TODO: this will need changes when switching to numeric IDs
|
||||||
|
|
||||||
|
usernames = items.filter_map { |uri| ActivityPub::TagManager.instance.uri_to_local_id(uri, :username)&.downcase }
|
||||||
|
Account.local.with_username(usernames)
|
||||||
|
end
|
||||||
|
|
||||||
def remove_unexpected_local_followers!
|
def remove_unexpected_local_followers!
|
||||||
@account.followers.local.where.not(id: @expected_followers.map(&:id)).reorder(nil).find_each do |unexpected_follower|
|
@account.followers.local.where.not(id: @expected_followers_ids).reorder(nil).find_each do |unexpected_follower|
|
||||||
UnfollowService.new.call(unexpected_follower, @account)
|
UnfollowService.new.call(unexpected_follower, @account)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_unexpected_outgoing_follows!
|
def handle_unexpected_outgoing_follows!(expected_followers)
|
||||||
@expected_followers.each do |expected_follower|
|
expected_followers.each do |expected_follower|
|
||||||
next if expected_follower.following?(@account)
|
next if expected_follower.following?(@account)
|
||||||
|
|
||||||
if expected_follower.requested?(@account)
|
if expected_follower.requested?(@account)
|
||||||
|
@ -50,21 +64,33 @@ class ActivityPub::SynchronizeFollowersService < BaseService
|
||||||
Oj.dump(serialize_payload(follow, ActivityPub::UndoFollowSerializer))
|
Oj.dump(serialize_payload(follow, ActivityPub::UndoFollowSerializer))
|
||||||
end
|
end
|
||||||
|
|
||||||
def collection_items(collection_or_uri)
|
# Only returns true if the whole collection has been processed
|
||||||
collection = fetch_collection(collection_or_uri)
|
def process_collection!(collection_uri, max_pages: MAX_COLLECTION_PAGES)
|
||||||
return unless collection.is_a?(Hash)
|
collection = fetch_collection(collection_uri)
|
||||||
|
return false unless collection.is_a?(Hash)
|
||||||
|
|
||||||
collection = fetch_collection(collection['first']) if collection['first'].present?
|
collection = fetch_collection(collection['first']) if collection['first'].present?
|
||||||
return unless collection.is_a?(Hash)
|
|
||||||
|
|
||||||
# Abort if we'd have to paginate through more than one page of followers
|
while collection.is_a?(Hash)
|
||||||
return if collection['next'].present?
|
process_page!(as_array(collection_page_items(collection)))
|
||||||
|
|
||||||
|
max_pages -= 1
|
||||||
|
|
||||||
|
return true if collection['next'].blank? # We reached the end of the collection
|
||||||
|
return false if max_pages <= 0 # We reached our pages limit
|
||||||
|
|
||||||
|
collection = fetch_collection(collection['next'])
|
||||||
|
end
|
||||||
|
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def collection_page_items(collection)
|
||||||
case collection['type']
|
case collection['type']
|
||||||
when 'Collection', 'CollectionPage'
|
when 'Collection', 'CollectionPage'
|
||||||
as_array(collection['items'])
|
collection['items']
|
||||||
when 'OrderedCollection', 'OrderedCollectionPage'
|
when 'OrderedCollection', 'OrderedCollectionPage'
|
||||||
as_array(collection['orderedItems'])
|
collection['orderedItems']
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,8 @@ ko:
|
||||||
too_soon: 너무 이릅니다. %{date} 이후로 지정해야 합니다
|
too_soon: 너무 이릅니다. %{date} 이후로 지정해야 합니다
|
||||||
user:
|
user:
|
||||||
attributes:
|
attributes:
|
||||||
|
date_of_birth:
|
||||||
|
below_limit: 나이 제한보다 아래입니다
|
||||||
email:
|
email:
|
||||||
blocked: 허용되지 않은 이메일 제공자입니다
|
blocked: 허용되지 않은 이메일 제공자입니다
|
||||||
unreachable: 존재하지 않는 것 같습니다
|
unreachable: 존재하지 않는 것 같습니다
|
||||||
|
|
|
@ -47,6 +47,7 @@ br:
|
||||||
demote: Argilañ
|
demote: Argilañ
|
||||||
disable: Skornañ
|
disable: Skornañ
|
||||||
disabled: Skornet
|
disabled: Skornet
|
||||||
|
display_name: Anv diskouezet
|
||||||
domain: Domani
|
domain: Domani
|
||||||
edit: Kemmañ
|
edit: Kemmañ
|
||||||
email: Postel
|
email: Postel
|
||||||
|
@ -66,6 +67,7 @@ br:
|
||||||
moderation:
|
moderation:
|
||||||
active: Oberiant
|
active: Oberiant
|
||||||
all: Pep tra
|
all: Pep tra
|
||||||
|
disabled: Diweredekaet
|
||||||
pending: War ober
|
pending: War ober
|
||||||
silenced: Bevennet
|
silenced: Bevennet
|
||||||
suspended: Astalet
|
suspended: Astalet
|
||||||
|
@ -98,6 +100,7 @@ br:
|
||||||
action_logs:
|
action_logs:
|
||||||
action_types:
|
action_types:
|
||||||
destroy_status: Dilemel ar c'hannad
|
destroy_status: Dilemel ar c'hannad
|
||||||
|
reset_password_user: Adderaouekaat ar ger-tremen
|
||||||
update_status: Hizivaat ar c'hannad
|
update_status: Hizivaat ar c'hannad
|
||||||
actions:
|
actions:
|
||||||
destroy_status_html: Dilamet eo bet toud %{target} gant %{name}
|
destroy_status_html: Dilamet eo bet toud %{target} gant %{name}
|
||||||
|
|
|
@ -85,25 +85,25 @@ lv:
|
||||||
title: Drošības atslēgas iespējotas
|
title: Drošības atslēgas iespējotas
|
||||||
omniauth_callbacks:
|
omniauth_callbacks:
|
||||||
failure: Nevarēja autentificēt tevi no %{kind}, jo "%{reason}".
|
failure: Nevarēja autentificēt tevi no %{kind}, jo "%{reason}".
|
||||||
success: Veiksmīgi autentificēts no %{kind} konta.
|
success: Sekmīgi autentificēts no %{kind} konta.
|
||||||
passwords:
|
passwords:
|
||||||
no_token: Tu nevari piekļūt šai lapai, ja neesi saņēmis paroles atiestatīšanas e-pasta ziņojumu. Ja ienāci no paroles atiestatīšanas e-pasta, lūdzu, pārliecinies, vai izmanto visu norādīto URL.
|
no_token: Tu nevari piekļūt šai lapai, ja neesi saņēmis paroles atiestatīšanas e-pasta ziņojumu. Ja ienāci no paroles atiestatīšanas e-pasta, lūdzu, pārliecinies, vai izmanto visu norādīto URL.
|
||||||
send_instructions: Ja Tava e-pasta adrese ir mūsu datubāzē, pēc dažām minūtēm savā e-pasta adresē saņemsi paroles atkopes saiti. Lūgums pārbaudīt mēstuļu mapi, ja nesaņēmi šo e-pasta ziņojumu.
|
send_instructions: Ja Tava e-pasta adrese ir mūsu datubāzē, pēc dažām minūtēm savā e-pasta adresē saņemsi paroles atkopes saiti. Lūgums pārbaudīt mēstuļu mapi, ja nesaņēmi šo e-pasta ziņojumu.
|
||||||
send_paranoid_instructions: Ja Tava e-pasta adrese ir mūsu datubāzē, pēc dažām minūtēm savā e-pasta adresē saņemsi paroles atkopes saiti. Lūgums pārbaudīt mēstuļu mapi, ja nesaņēmi šo e-pasta ziņojumu.
|
send_paranoid_instructions: Ja Tava e-pasta adrese ir mūsu datubāzē, pēc dažām minūtēm savā e-pasta adresē saņemsi paroles atkopes saiti. Lūgums pārbaudīt mēstuļu mapi, ja nesaņēmi šo e-pasta ziņojumu.
|
||||||
updated: Tava parole tika veiksmīgi nomainīta. Tagad esi pieteicies.
|
updated: Tava parole tika sekmīgi nomainīta. Tagad esi pieteicies.
|
||||||
updated_not_active: Tava parole ir veiksmīgi nomainīta.
|
updated_not_active: Tava parole tika sekmīgi nomainīta.
|
||||||
registrations:
|
registrations:
|
||||||
destroyed: Visu labu! Tavs konts ir veiksmīgi atcelts. Mēs ceram tevi drīz atkal redzēt.
|
destroyed: Visu labu! Tavs konts ir sekmīgi atcelts. Mēs ceram Tevi drīz atkal redzēt.
|
||||||
update_needs_confirmation: Tu veiksmīgi atjaunināji savu kontu, taču mums ir jāapliecina Tava jaunā e-pasta adrese. Lūgums pārbaudīt savu e-pastu un sekot apstiprinājuma saitei, lai apstiprinātu savu jauno e-pasta adresi. Lūgums pārbaudīt mēstuļu mapi, ja nesaņēmi šo e-pasta ziņojumu.
|
update_needs_confirmation: Tu sekmīgi atjaunināji savu kontu, taču mums ir jāapliecina Tava jaunā e-pasta adrese. Lūgums pārbaudīt savu e-pastu un sekot apstiprinājuma saitei, lai apstiprinātu savu jauno e-pasta adresi. Lūgums pārbaudīt mēstuļu mapi, ja nesaņēmi šo e-pasta ziņojumu.
|
||||||
updated: Tavs konts ir veiksmīgi atjaunināts.
|
updated: Tavs konts tika sekmīgi atjaunināts.
|
||||||
sessions:
|
sessions:
|
||||||
already_signed_out: Veiksmīgi izrakstījies.
|
already_signed_out: Sekmīgi izrakstījies.
|
||||||
signed_in: Veiksmīgi pieteicies.
|
signed_in: Sekmīgi pierakstījies.
|
||||||
signed_out: Veiksmīgi izrakstījies.
|
signed_out: Sekmīgi izrakstījies.
|
||||||
unlocks:
|
unlocks:
|
||||||
send_instructions: Pēc dažām minūtēm Tu saņemsi e-pasta ziņojumu ar norādēm, kā atslēgt savu kontu. Lūgums pārbaudīt mēstuļu mapi, ja nesaņēmi šo e-pasta ziņojumu.
|
send_instructions: Pēc dažām minūtēm Tu saņemsi e-pasta ziņojumu ar norādēm, kā atslēgt savu kontu. Lūgums pārbaudīt mēstuļu mapi, ja nesaņēmi šo e-pasta ziņojumu.
|
||||||
send_paranoid_instructions: Ja Tavs konts pastāv, dažu minūšu laikā saņemsi e-pasta ziņojumu ar norādēm, kā to atslēgt. Lūgums pārbaudīt mēstuļu mapi, ja nesaņēmi šo e-pasta ziņojumu.
|
send_paranoid_instructions: Ja Tavs konts pastāv, dažu minūšu laikā saņemsi e-pasta ziņojumu ar norādēm, kā to atslēgt. Lūgums pārbaudīt mēstuļu mapi, ja nesaņēmi šo e-pasta ziņojumu.
|
||||||
unlocked: Konts tika veiksmīgi atbloķēts. Lūgums pieteikties, lai turpinātu.
|
unlocked: Konts tika sekmīgi atslēgts. Lūgums pieteikties, lai turpinātu.
|
||||||
errors:
|
errors:
|
||||||
messages:
|
messages:
|
||||||
already_confirmed: jau tika apstiprināts, lūgums mēģināt pieteikties
|
already_confirmed: jau tika apstiprināts, lūgums mēģināt pieteikties
|
||||||
|
|
|
@ -153,7 +153,7 @@ ko:
|
||||||
position: 특정 상황에서 충돌이 발생할 경우 더 높은 역할이 충돌을 해결합니다. 특정 작업은 우선순위가 낮은 역할에 대해서만 수행될 수 있습니다
|
position: 특정 상황에서 충돌이 발생할 경우 더 높은 역할이 충돌을 해결합니다. 특정 작업은 우선순위가 낮은 역할에 대해서만 수행될 수 있습니다
|
||||||
webhook:
|
webhook:
|
||||||
events: 전송할 이벤트를 선택하세요
|
events: 전송할 이벤트를 선택하세요
|
||||||
template: 원하는 JSON 페이로드를 변수와 함께 작성하거나, 그냥 냅둬서 기본 JSON을 사용할 수 있습니다.
|
template: 원하는 JSON 페이로드를 변수와 함께 작성하거나, 그대로 두어 기본 JSON을 사용할 수 있습니다.
|
||||||
url: 이벤트가 어디로 전송될 지
|
url: 이벤트가 어디로 전송될 지
|
||||||
labels:
|
labels:
|
||||||
account:
|
account:
|
||||||
|
@ -269,6 +269,7 @@ ko:
|
||||||
favicon: 파비콘
|
favicon: 파비콘
|
||||||
mascot: 사용자 정의 마스코트 (legacy)
|
mascot: 사용자 정의 마스코트 (legacy)
|
||||||
media_cache_retention_period: 미디어 캐시 유지 기한
|
media_cache_retention_period: 미디어 캐시 유지 기한
|
||||||
|
min_age: 최소 연령 제한
|
||||||
peers_api_enabled: API에 발견 된 서버들의 목록 발행
|
peers_api_enabled: API에 발견 된 서버들의 목록 발행
|
||||||
profile_directory: 프로필 책자 활성화
|
profile_directory: 프로필 책자 활성화
|
||||||
registrations_mode: 누가 가입할 수 있는지
|
registrations_mode: 누가 가입할 수 있는지
|
||||||
|
@ -347,6 +348,9 @@ ko:
|
||||||
jurisdiction: 법적 관할권
|
jurisdiction: 법적 관할권
|
||||||
min_age: 최소 연령
|
min_age: 최소 연령
|
||||||
user:
|
user:
|
||||||
|
date_of_birth_1i: 일
|
||||||
|
date_of_birth_2i: 월
|
||||||
|
date_of_birth_3i: 년
|
||||||
role: 역할
|
role: 역할
|
||||||
time_zone: 시간대
|
time_zone: 시간대
|
||||||
user_role:
|
user_role:
|
||||||
|
|
|
@ -169,7 +169,7 @@ RSpec.describe 'Notifications' do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with min_id param' do
|
context 'with min_id param' do
|
||||||
let(:params) { { min_id: user.account.notifications.reload.first.id - 1 } }
|
let(:params) { { min_id: user.account.notifications.order(id: :asc).first.id - 1 } }
|
||||||
|
|
||||||
it 'returns a notification group covering all notifications' do
|
it 'returns a notification group covering all notifications' do
|
||||||
subject
|
subject
|
||||||
|
|
|
@ -10,7 +10,7 @@ RSpec.describe ActivityPub::SynchronizeFollowersService do
|
||||||
let(:bob) { Fabricate(:account, username: 'bob') }
|
let(:bob) { Fabricate(:account, username: 'bob') }
|
||||||
let(:eve) { Fabricate(:account, username: 'eve') }
|
let(:eve) { Fabricate(:account, username: 'eve') }
|
||||||
let(:mallory) { Fabricate(:account, username: 'mallory') }
|
let(:mallory) { Fabricate(:account, username: 'mallory') }
|
||||||
let(:collection_uri) { 'http://example.com/partial-followers' }
|
let(:collection_uri) { 'https://example.com/partial-followers' }
|
||||||
|
|
||||||
let(:items) do
|
let(:items) do
|
||||||
[alice, eve, mallory].map do |account|
|
[alice, eve, mallory].map do |account|
|
||||||
|
@ -97,7 +97,76 @@ RSpec.describe ActivityPub::SynchronizeFollowersService do
|
||||||
it_behaves_like 'synchronizes followers'
|
it_behaves_like 'synchronizes followers'
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the endpoint is a paginated Collection of actor URIs with a next page' do
|
context 'when the endpoint is a paginated Collection of actor URIs split across multiple pages' do
|
||||||
|
before do
|
||||||
|
stub_request(:get, 'https://example.com/partial-followers')
|
||||||
|
.to_return(status: 200, headers: { 'Content-Type': 'application/activity+json' }, body: Oj.dump({
|
||||||
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||||
|
type: 'Collection',
|
||||||
|
id: 'https://example.com/partial-followers',
|
||||||
|
first: 'https://example.com/partial-followers/1',
|
||||||
|
}))
|
||||||
|
|
||||||
|
stub_request(:get, 'https://example.com/partial-followers/1')
|
||||||
|
.to_return(status: 200, headers: { 'Content-Type': 'application/activity+json' }, body: Oj.dump({
|
||||||
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||||
|
type: 'CollectionPage',
|
||||||
|
id: 'https://example.com/partial-followers/1',
|
||||||
|
partOf: 'https://example.com/partial-followers',
|
||||||
|
next: 'https://example.com/partial-followers/2',
|
||||||
|
items: [alice, eve].map { |account| ActivityPub::TagManager.instance.uri_for(account) },
|
||||||
|
}))
|
||||||
|
|
||||||
|
stub_request(:get, 'https://example.com/partial-followers/2')
|
||||||
|
.to_return(status: 200, headers: { 'Content-Type': 'application/activity+json' }, body: Oj.dump({
|
||||||
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||||
|
type: 'CollectionPage',
|
||||||
|
id: 'https://example.com/partial-followers/2',
|
||||||
|
partOf: 'https://example.com/partial-followers',
|
||||||
|
items: ActivityPub::TagManager.instance.uri_for(mallory),
|
||||||
|
}))
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'synchronizes followers'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the endpoint is a paginated Collection of actor URIs split across, but one page errors out' do
|
||||||
|
before do
|
||||||
|
stub_request(:get, 'https://example.com/partial-followers')
|
||||||
|
.to_return(status: 200, headers: { 'Content-Type': 'application/activity+json' }, body: Oj.dump({
|
||||||
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||||
|
type: 'Collection',
|
||||||
|
id: 'https://example.com/partial-followers',
|
||||||
|
first: 'https://example.com/partial-followers/1',
|
||||||
|
}))
|
||||||
|
|
||||||
|
stub_request(:get, 'https://example.com/partial-followers/1')
|
||||||
|
.to_return(status: 200, headers: { 'Content-Type': 'application/activity+json' }, body: Oj.dump({
|
||||||
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||||
|
type: 'CollectionPage',
|
||||||
|
id: 'https://example.com/partial-followers/1',
|
||||||
|
partOf: 'https://example.com/partial-followers',
|
||||||
|
next: 'https://example.com/partial-followers/2',
|
||||||
|
items: [mallory].map { |account| ActivityPub::TagManager.instance.uri_for(account) },
|
||||||
|
}))
|
||||||
|
|
||||||
|
stub_request(:get, 'https://example.com/partial-followers/2')
|
||||||
|
.to_return(status: 404)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'confirms pending follow request but does not remove extra followers' do
|
||||||
|
previous_follower_ids = actor.followers.pluck(:id)
|
||||||
|
|
||||||
|
subject.call(actor, collection_uri)
|
||||||
|
|
||||||
|
expect(previous_follower_ids - actor.followers.reload.pluck(:id))
|
||||||
|
.to be_empty
|
||||||
|
expect(mallory)
|
||||||
|
.to be_following(actor)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the endpoint is a paginated Collection of actor URIs with more pages than we allow' do
|
||||||
let(:payload) do
|
let(:payload) do
|
||||||
{
|
{
|
||||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||||
|
@ -113,12 +182,19 @@ RSpec.describe ActivityPub::SynchronizeFollowersService do
|
||||||
end
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
stub_const('ActivityPub::SynchronizeFollowersService::MAX_COLLECTION_PAGES', 1)
|
||||||
stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' })
|
stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' })
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not change followers' do
|
it 'confirms pending follow request but does not remove extra followers' do
|
||||||
expect { subject.call(actor, collection_uri) }
|
previous_follower_ids = actor.followers.pluck(:id)
|
||||||
.to_not(change { actor.followers.reload.reorder(id: :asc).pluck(:id) })
|
|
||||||
|
subject.call(actor, collection_uri)
|
||||||
|
|
||||||
|
expect(previous_follower_ids - actor.followers.reload.pluck(:id))
|
||||||
|
.to be_empty
|
||||||
|
expect(mallory)
|
||||||
|
.to be_following(actor)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Reference in a new issue