From 0dd4595704896fecb69315c1617dcb74b1cfaf67 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Tue, 9 Jul 2024 09:27:13 +0200
Subject: [PATCH 1/8] fix(deps): update dependency glob to v10.4.4 (#30967)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
 yarn.lock | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/yarn.lock b/yarn.lock
index 87586a916c6..e377c52129c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -9009,8 +9009,8 @@ __metadata:
   linkType: hard
 
 "glob@npm:^10.2.2, glob@npm:^10.2.6, glob@npm:^10.3.10":
-  version: 10.4.3
-  resolution: "glob@npm:10.4.3"
+  version: 10.4.4
+  resolution: "glob@npm:10.4.4"
   dependencies:
     foreground-child: "npm:^3.1.0"
     jackspeak: "npm:^3.1.2"
@@ -9020,7 +9020,7 @@ __metadata:
     path-scurry: "npm:^1.11.1"
   bin:
     glob: dist/esm/bin.mjs
-  checksum: 10c0/bea148e5dae96c17e2764f4764c72376a6ab7072b27a21e861ae4af6f97f3e810d79d67f64de52f63ce1d7fdb73b7306f61c65b48d0f61ca7c8647ce8acaf9a7
+  checksum: 10c0/8f0887ae6b9e7ec97841c88f3189643a326c9c37f9881050979c131a2198f2230d4b0a196b71ec6a6694871c25fb8631a72af6e2ea941a667c55f155765546ab
   languageName: node
   linkType: hard
 

From 7a30c689530c93e8ea473e39cfb2e2ff6d9d2807 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Tue, 9 Jul 2024 03:34:15 -0400
Subject: [PATCH 2/8] Use `scope module: ...` block for `.well-known` routes
 (#30959)

---
 config/routes.rb | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/config/routes.rb b/config/routes.rb
index 4b3bd4f187a..5862731ba98 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -63,12 +63,16 @@ Rails.application.routes.draw do
                 tokens: 'oauth/tokens'
   end
 
-  get '.well-known/oauth-authorization-server', to: 'well_known/oauth_metadata#show', as: :oauth_metadata, defaults: { format: 'json' }
-  get '.well-known/host-meta', to: 'well_known/host_meta#show', as: :host_meta, defaults: { format: 'xml' }
-  get '.well-known/nodeinfo', to: 'well_known/node_info#index', as: :nodeinfo, defaults: { format: 'json' }
-  get '.well-known/webfinger', to: 'well_known/webfinger#show', as: :webfinger
-  get '.well-known/change-password', to: redirect('/auth/edit')
-  get '.well-known/proxy', to: redirect { |_, request| "/authorize_interaction?#{request.params.to_query}" }
+  scope path: '.well-known' do
+    scope module: :well_known do
+      get 'oauth-authorization-server', to: 'oauth_metadata#show', as: :oauth_metadata, defaults: { format: 'json' }
+      get 'host-meta', to: 'host_meta#show', as: :host_meta, defaults: { format: 'xml' }
+      get 'nodeinfo', to: 'node_info#index', as: :nodeinfo, defaults: { format: 'json' }
+      get 'webfinger', to: 'webfinger#show', as: :webfinger
+    end
+    get 'change-password', to: redirect('/auth/edit'), as: nil
+    get 'proxy', to: redirect { |_, request| "/authorize_interaction?#{request.params.to_query}" }, as: nil
+  end
 
   get '/nodeinfo/2.0', to: 'well_known/node_info#show', as: :nodeinfo_schema
 

From f47d761e12a16c4338447aa8dbeca8365ee812cc Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Tue, 9 Jul 2024 03:34:19 -0400
Subject: [PATCH 3/8] Remove unneeded `controller` option in routes (#30958)

---
 config/routes.rb          | 2 +-
 config/routes/settings.rb | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/config/routes.rb b/config/routes.rb
index 5862731ba98..2c06762c229 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -98,7 +98,7 @@ Rails.application.routes.draw do
 
     namespace :auth do
       resource :setup, only: [:show, :update], controller: :setup
-      resource :challenge, only: [:create], controller: :challenges
+      resource :challenge, only: [:create]
       get 'sessions/security_key_options', to: 'sessions#webauthn_options'
       post 'captcha_confirmation', to: 'confirmations#confirm_captcha', as: :captcha_confirmation
     end
diff --git a/config/routes/settings.rb b/config/routes/settings.rb
index b14606656b6..297b80942c9 100644
--- a/config/routes/settings.rb
+++ b/config/routes/settings.rb
@@ -26,9 +26,9 @@ namespace :settings do
     resources :follows, only: :index, controller: :following_accounts
     resources :blocks, only: :index, controller: :blocked_accounts
     resources :mutes, only: :index, controller: :muted_accounts
-    resources :lists, only: :index, controller: :lists
+    resources :lists, only: :index
     resources :domain_blocks, only: :index, controller: :blocked_domains
-    resources :bookmarks, only: :index, controller: :bookmarks
+    resources :bookmarks, only: :index
   end
 
   resources :two_factor_authentication_methods, only: [:index] do

From 249b4117f9fbdac3888d3807a81c0e83e39d7de5 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
 <41898282+github-actions[bot]@users.noreply.github.com>
Date: Tue, 9 Jul 2024 07:36:13 +0000
Subject: [PATCH 4/8] New Crowdin Translations (automated) (#30970)

Co-authored-by: GitHub Actions <noreply@github.com>
---
 app/javascript/mastodon/locales/ry.json | 39 +++++++++++++++++++++++--
 1 file changed, 37 insertions(+), 2 deletions(-)

diff --git a/app/javascript/mastodon/locales/ry.json b/app/javascript/mastodon/locales/ry.json
index 67aad910054..f3a68b15a12 100644
--- a/app/javascript/mastodon/locales/ry.json
+++ b/app/javascript/mastodon/locales/ry.json
@@ -3,6 +3,8 @@
   "about.contact": "Контакт:",
   "about.disclaimer": "Mastodon є задарьнов проґрамов из удпертым кодом тай торговов значков Mastodon gGmbH.",
   "about.domain_blocks.no_reason_available": "Причины не ясні",
+  "about.domain_blocks.preamble": "Майбульш Mastodon поволят вам позирати контент тай комуніковати из хосновачами из другых федерованых серверув. Туй лиш уняткы учинені про сись конкретный сервер.",
+  "about.domain_blocks.silenced.explanation": "Вы майбульш не будете видіти профілі тай контент из сього сервера, кидь не будете го самі глядати авадь пудпишете ся на нього.",
   "about.domain_blocks.silenced.title": "Обмежено",
   "about.domain_blocks.suspended.explanation": "Ниякі податкы из сього сервера не будут уброблені, усокочені ци поміняні, што чинит невозможнов хоть-яку інтеракцію ци зязок из хосновачами из сього сервера.",
   "about.domain_blocks.suspended.title": "Заблоковано",
@@ -20,6 +22,7 @@
   "account.browse_more_on_origin_server": "Позирайте бульше на ориґіналнум профілю",
   "account.cancel_follow_request": "Удмінити пудписку",
   "account.copy": "Зкопіровати удкликованя на профіл",
+  "account.direct": "Пошептати @{name}",
   "account.disable_notifications": "Бульше не сповіщати ми коли {name} пише",
   "account.domain_blocked": "Домен заблокованый",
   "account.edit_profile": "Управити профіл",
@@ -39,8 +42,10 @@
   "account.joined_short": "Датум прикапчованя",
   "account.languages": "Поміняти убрані языкы",
   "account.link_verified_on": "Властность сього удкликованя было звірено {date}",
+  "account.locked_info": "Сись профіл є замкнутый. Ґазда акаунта буде ручно провіряти тко го може зафоловити.",
   "account.media": "Медіа",
-  "account.moved_to": "Хосновач {name} указав, ож новый профіл йим є:",
+  "account.mention": "Спомянути @{name}",
+  "account.moved_to": "Хосновач {name} указав, ож новый профіл му є:",
   "account.mute": "Стишити {name}",
   "account.mute_notifications_short": "Стишити голошіня",
   "account.mute_short": "Стишити",
@@ -60,9 +65,12 @@
   "account.unblock_short": "Розблоковати",
   "account.unendorse": "Не указовати на профілови",
   "account.unfollow": "Удписати ся",
+  "account.unmute": "Указовати {name}",
   "account.unmute_notifications_short": "Указовати голошіня",
   "account.unmute_short": "Указовати",
   "account_note.placeholder": "Клопкніт обы додати примітку",
+  "admin.dashboard.retention.average": "Середньоє",
+  "admin.dashboard.retention.cohort": "Місяць прикапчованя",
   "admin.dashboard.retention.cohort_size": "Нові хосновачі",
   "admin.impact_report.instance_accounts": "Профілі из акаунтув, котрі ся удалят",
   "admin.impact_report.instance_followers": "Пудписникы, котрых стратят наші хосновачі",
@@ -70,11 +78,38 @@
   "admin.impact_report.title": "Вплыв цілком",
   "alert.rate_limited.message": "Попробуйте зась по {retry_time, time, medium}.",
   "alert.rate_limited.title": "Частота обмежена",
+  "alert.unexpected.message": "Стала ся нечекана хыба.",
+  "alert.unexpected.title": "Ийой!",
+  "announcement.announcement": "Голошіня",
+  "audio.hide": "Зпрятати звук",
+  "block_modal.show_less": "Указати менше",
+  "block_modal.show_more": "Указати бульше",
+  "block_modal.they_cant_mention": "Они не можут вас споминати авадь слідовати.",
+  "block_modal.they_cant_see_posts": "Они не можут видіти ваші публикації, тай наспак — вы йихні.",
+  "block_modal.they_will_know": "Они видят, ож сут заблоковані.",
+  "block_modal.title": "Заблоковати хосновача?",
+  "block_modal.you_wont_see_mentions": "Не будете видіти публикації тай споминкы сього хосновача.",
+  "boost_modal.combo": "Можете клынцнути {combo} другый раз обы сесе пропустити",
+  "bundle_column_error.copy_stacktrace": "Укопіровати звіт за хыбу",
+  "bundle_column_error.error.body": "Не годни сьме указати зажадану сторунку. Годно быти спозад хыбы у нашум сістемі, авадь проблемы зумісности бравзера.",
+  "bundle_column_error.error.title": "Ийой!",
+  "bundle_column_error.network.body": "Стала ся хыба як сьме пробовали напаровати сторунку. Годно ся йсе было стати спозад слабого споєня вашого інтернета, авадь сервера.",
+  "bundle_column_error.network.title": "Хыба споєня",
+  "bundle_column_error.retry": "Попробуйте зась",
   "bundle_column_error.return": "Вернути ся на головну",
   "bundle_column_error.routing.body": "Не можеме найти сяку сторунку. Бизувні сьте, ож URL у адресному шорикови є добрый?",
   "bundle_column_error.routing.title": "404",
   "bundle_modal_error.close": "Заперти",
   "bundle_modal_error.message": "Штось ся показило, закидь сьме ладовали сись компонент.",
   "bundle_modal_error.retry": "Попробовати зась",
-  "closed_registrations.other_server_instructions": "Mastodon є децентралізованов платформов, можете си учинити профіл и на другому серверови тай комуніковати из сим."
+  "closed_registrations.other_server_instructions": "Mastodon є децентралізованов платформов, можете си учинити профіл и на другому серверови тай комуніковати из сим.",
+  "closed_registrations_modal.description": "Раз не мож учинити профіл на {domain}, айбо не мусите мати профіл ипен на серверови {domain} обы хосновати Mastodon.",
+  "closed_registrations_modal.find_another_server": "Найти другый сервер",
+  "column.about": "За сайт",
+  "column.blocks": "Заблоковані хосновачі",
+  "column.bookmarks": "Усокоченоє",
+  "column.direct": "Шептаня",
+  "column.directory": "Никати профілі",
+  "column.domain_blocks": "Заблоковані домены",
+  "column.favourites": "Убраноє"
 }

From 7542a134d5fecf82d14dcc1860be87cf6645ec7f Mon Sep 17 00:00:00 2001
From: Renaud Chaput <renchap@gmail.com>
Date: Tue, 9 Jul 2024 12:47:08 +0200
Subject: [PATCH 5/8] Add a file for Sidekiq to signal it is ready to process
 jobs (#30971)

---
 config/initializers/sidekiq.rb | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb
index 53b02edc402..e1d98b8ba3b 100644
--- a/config/initializers/sidekiq.rb
+++ b/config/initializers/sidekiq.rb
@@ -2,9 +2,21 @@
 
 require_relative '../../lib/mastodon/sidekiq_middleware'
 
+SIDEKIQ_WILL_PROCESSES_JOBS_FILE = Rails.root.join('tmp', 'sidekiq_process_has_started_and_will_begin_processing_jobs').freeze
+
 Sidekiq.configure_server do |config|
   config.redis = REDIS_SIDEKIQ_PARAMS
 
+  # This is used in Kubernetes setups, to signal that the Sidekiq process has started and will begin processing jobs
+  # This comes from https://github.com/sidekiq/sidekiq/wiki/Kubernetes#sidekiq
+  config.on(:startup) do
+    FileUtils.touch(SIDEKIQ_WILL_PROCESSES_JOBS_FILE)
+  end
+
+  config.on(:shutdown) do
+    FileUtils.rm_f(SIDEKIQ_WILL_PROCESSES_JOBS_FILE)
+  end
+
   config.server_middleware do |chain|
     chain.add Mastodon::SidekiqMiddleware
   end

From 3875bd138d279eb26efce9f446fe91f49cf38f55 Mon Sep 17 00:00:00 2001
From: Daniel M Brasil <danielmbrasil@protonmail.com>
Date: Tue, 9 Jul 2024 09:41:49 -0300
Subject: [PATCH 6/8] Fix HTTP 500 in `/api/v1/polls/:id/votes` (#25598)

---
 app/controllers/api/v1/polls/votes_controller.rb |  4 ++--
 spec/requests/api/v1/polls/votes_spec.rb         | 11 ++++++++++-
 2 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/app/controllers/api/v1/polls/votes_controller.rb b/app/controllers/api/v1/polls/votes_controller.rb
index 513b937ef2d..ad1b82cb522 100644
--- a/app/controllers/api/v1/polls/votes_controller.rb
+++ b/app/controllers/api/v1/polls/votes_controller.rb
@@ -8,7 +8,7 @@ class Api::V1::Polls::VotesController < Api::BaseController
   before_action :set_poll
 
   def create
-    VoteService.new.call(current_account, @poll, vote_params[:choices])
+    VoteService.new.call(current_account, @poll, vote_params)
     render json: @poll, serializer: REST::PollSerializer
   end
 
@@ -22,6 +22,6 @@ class Api::V1::Polls::VotesController < Api::BaseController
   end
 
   def vote_params
-    params.permit(choices: [])
+    params.require(:choices)
   end
 end
diff --git a/spec/requests/api/v1/polls/votes_spec.rb b/spec/requests/api/v1/polls/votes_spec.rb
index e2b22708be8..669f64b6e45 100644
--- a/spec/requests/api/v1/polls/votes_spec.rb
+++ b/spec/requests/api/v1/polls/votes_spec.rb
@@ -10,9 +10,10 @@ RSpec.describe 'API V1 Polls Votes' do
 
   describe 'POST /api/v1/polls/:poll_id/votes' do
     let(:poll) { Fabricate(:poll) }
+    let(:params) { { choices: %w(1) } }
 
     before do
-      post "/api/v1/polls/#{poll.id}/votes", params: { choices: %w(1) }, headers: headers
+      post "/api/v1/polls/#{poll.id}/votes", params: params, headers: headers
     end
 
     it 'creates a vote', :aggregate_failures do
@@ -24,6 +25,14 @@ RSpec.describe 'API V1 Polls Votes' do
       expect(poll.reload.cached_tallies).to eq [0, 1]
     end
 
+    context 'when the required choices param is not provided' do
+      let(:params) { {} }
+
+      it 'returns http bad request' do
+        expect(response).to have_http_status(400)
+      end
+    end
+
     private
 
     def vote

From ef2e48e6dac1ed327fc6ac07b0adf252d10ff607 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Tue, 9 Jul 2024 14:53:31 +0200
Subject: [PATCH 7/8] fix(deps): update dependency glob to v10.4.5 (#30972)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
 yarn.lock | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/yarn.lock b/yarn.lock
index e377c52129c..85a336a4a34 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -9009,8 +9009,8 @@ __metadata:
   linkType: hard
 
 "glob@npm:^10.2.2, glob@npm:^10.2.6, glob@npm:^10.3.10":
-  version: 10.4.4
-  resolution: "glob@npm:10.4.4"
+  version: 10.4.5
+  resolution: "glob@npm:10.4.5"
   dependencies:
     foreground-child: "npm:^3.1.0"
     jackspeak: "npm:^3.1.2"
@@ -9020,7 +9020,7 @@ __metadata:
     path-scurry: "npm:^1.11.1"
   bin:
     glob: dist/esm/bin.mjs
-  checksum: 10c0/8f0887ae6b9e7ec97841c88f3189643a326c9c37f9881050979c131a2198f2230d4b0a196b71ec6a6694871c25fb8631a72af6e2ea941a667c55f155765546ab
+  checksum: 10c0/19a9759ea77b8e3ca0a43c2f07ecddc2ad46216b786bb8f993c445aee80d345925a21e5280c7b7c6c59e860a0154b84e4b2b60321fea92cd3c56b4a7489f160e
   languageName: node
   linkType: hard
 

From 967505ee9bcacf0e5189aa06c654ff586c198a46 Mon Sep 17 00:00:00 2001
From: David Roetzel <david@roetzel.de>
Date: Tue, 9 Jul 2024 15:11:34 +0200
Subject: [PATCH 8/8] Add size limit for all PreviewCard URLs (#30973)

---
 app/models/preview_card.rb                    |  7 +++++-
 app/services/fetch_link_card_service.rb       |  7 ++----
 spec/fixtures/requests/long_canonical_url.txt | 18 +++++++++++++++
 spec/services/fetch_link_card_service_spec.rb | 22 ++++++++-----------
 4 files changed, 35 insertions(+), 19 deletions(-)
 create mode 100644 spec/fixtures/requests/long_canonical_url.txt

diff --git a/app/models/preview_card.rb b/app/models/preview_card.rb
index eac02ac14ff..5a11351e588 100644
--- a/app/models/preview_card.rb
+++ b/app/models/preview_card.rb
@@ -46,6 +46,11 @@ class PreviewCard < ApplicationRecord
     y_comp: 4,
   }.freeze
 
+  # URL size limit to safely store in PosgreSQL's unique indexes
+  # Technically this is a byte-size limit but we use it as a
+  # character limit to work with length validation
+  URL_CHARACTER_LIMIT = 2692
+
   self.inheritance_column = false
 
   enum :type, { link: 0, photo: 1, video: 2, rich: 3 }
@@ -63,7 +68,7 @@ class PreviewCard < ApplicationRecord
                     convert_options: { all: '-quality 90 +profile "!icc,*" +set date:modify +set date:create +set date:timestamp' },
                     validate_media_type: false
 
-  validates :url, presence: true, uniqueness: true, url: true
+  validates :url, presence: true, uniqueness: true, url: true, length: { maximum: URL_CHARACTER_LIMIT }
   validates_attachment_content_type :image, content_type: IMAGE_MIME_TYPES
   validates_attachment_size :image, less_than: LIMIT
   remotable_attachment :image, LIMIT
diff --git a/app/services/fetch_link_card_service.rb b/app/services/fetch_link_card_service.rb
index 436e024c99b..adabb1096e8 100644
--- a/app/services/fetch_link_card_service.rb
+++ b/app/services/fetch_link_card_service.rb
@@ -15,9 +15,6 @@ class FetchLinkCardService < BaseService
     )
   }iox
 
-  # URL size limit to safely store in PosgreSQL's unique indexes
-  BYTESIZE_LIMIT = 2692
-
   def call(status)
     @status       = status
     @original_url = parse_urls
@@ -32,7 +29,7 @@ class FetchLinkCardService < BaseService
     end
 
     attach_card if @card&.persisted?
-  rescue HTTP::Error, OpenSSL::SSL::SSLError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError, EncodingError => e
+  rescue HTTP::Error, OpenSSL::SSL::SSLError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError, EncodingError, ActiveRecord::RecordInvalid => e
     Rails.logger.debug { "Error fetching link #{@original_url}: #{e}" }
     nil
   end
@@ -88,7 +85,7 @@ class FetchLinkCardService < BaseService
 
   def bad_url?(uri)
     # Avoid local instance URLs and invalid URLs
-    uri.host.blank? || TagManager.instance.local_url?(uri.to_s) || !%w(http https).include?(uri.scheme) || uri.to_s.bytesize > BYTESIZE_LIMIT
+    uri.host.blank? || TagManager.instance.local_url?(uri.to_s) || !%w(http https).include?(uri.scheme)
   end
 
   def mention_link?(anchor)
diff --git a/spec/fixtures/requests/long_canonical_url.txt b/spec/fixtures/requests/long_canonical_url.txt
new file mode 100644
index 00000000000..97d6c93961f
--- /dev/null
+++ b/spec/fixtures/requests/long_canonical_url.txt
@@ -0,0 +1,18 @@
+HTTP/1.1 200 OK
+server: nginx
+date: Thu, 13 Jun 2024 14:33:13 GMT
+content-type: text/html; charset=utf-8
+content-length: 3225
+accept-ranges: bytes
+
+<!doctype html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <link rel="canonical" href="https://example.com/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">
+  <title>Very long canonical URL</title>
+</head>
+<body>
+  <h2>We have very long URLs</h2>
+</body>
+</html>
diff --git a/spec/services/fetch_link_card_service_spec.rb b/spec/services/fetch_link_card_service_spec.rb
index b2cd99cea6d..342902cdb3d 100644
--- a/spec/services/fetch_link_card_service_spec.rb
+++ b/spec/services/fetch_link_card_service_spec.rb
@@ -31,6 +31,7 @@ RSpec.describe FetchLinkCardService do
     stub_request(:get, 'http://example.com/latin1_posing_as_utf8_recoverable').to_return(request_fixture('latin1_posing_as_utf8_recoverable.txt'))
     stub_request(:get, 'http://example.com/aergerliche-umlaute').to_return(request_fixture('redirect_with_utf8_url.txt'))
     stub_request(:get, 'http://example.com/page_without_title').to_return(request_fixture('page_without_title.txt'))
+    stub_request(:get, 'http://example.com/long_canonical_url').to_return(request_fixture('long_canonical_url.txt'))
 
     Rails.cache.write('oembed_endpoint:example.com', oembed_cache) if oembed_cache
 
@@ -233,19 +234,6 @@ RSpec.describe FetchLinkCardService do
       end
     end
 
-    context 'with an URL too long for PostgreSQL unique indexes' do
-      let(:url) { "http://example.com/#{'a' * 2674}" }
-      let(:status) { Fabricate(:status, text: url) }
-
-      it 'does not fetch the URL' do
-        expect(a_request(:get, url)).to_not have_been_made
-      end
-
-      it 'does not create a preview card' do
-        expect(status.preview_card).to be_nil
-      end
-    end
-
     context 'with a URL of a page with oEmbed support' do
       let(:html) { '<!doctype html><title>Hello world</title><link rel="alternate" type="application/json+oembed" href="http://example.com/oembed?url=http://example.com/html">' }
       let(:status) { Fabricate(:status, text: 'http://example.com/html') }
@@ -296,6 +284,14 @@ RSpec.describe FetchLinkCardService do
         end
       end
     end
+
+    context 'with a URL of a page that includes a canonical URL too long for PostgreSQL unique indexes' do
+      let(:status) { Fabricate(:status, text: 'test http://example.com/long_canonical_url') }
+
+      it 'does not create a preview card' do
+        expect(status.preview_card).to be_nil
+      end
+    end
   end
 
   context 'with a remote status' do