From 5d849571176b2e7ec6ae55370bee89cbf0c1c57f Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 3 Dec 2025 12:00:41 +0100 Subject: [PATCH] Add shareable wrapstodon links (#37047) --- app/controllers/wrapstodon_controller.rb | 23 +++++++++++++++++++ app/lib/annual_report.rb | 3 ++- app/models/generated_annual_report.rb | 5 ++-- .../rest/annual_report_serializer.rb | 6 ++++- app/views/wrapstodon/show.html.haml | 9 ++++++++ config/locales/en.yml | 2 ++ config/routes.rb | 1 + ...d_share_key_to_generated_annual_reports.rb | 7 ++++++ db/schema.rb | 3 ++- 9 files changed, 54 insertions(+), 5 deletions(-) create mode 100644 app/controllers/wrapstodon_controller.rb create mode 100644 app/views/wrapstodon/show.html.haml create mode 100644 db/migrate/20251202140424_add_share_key_to_generated_annual_reports.rb diff --git a/app/controllers/wrapstodon_controller.rb b/app/controllers/wrapstodon_controller.rb new file mode 100644 index 0000000000..74f0dbb65a --- /dev/null +++ b/app/controllers/wrapstodon_controller.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class WrapstodonController < ApplicationController + include WebAppControllerConcern + include Authorization + include AccountOwnedConcern + + vary_by 'Accept, Accept-Language, Cookie' + + before_action :set_generated_annual_report + + skip_before_action :require_functional!, only: :show, unless: :limited_federation_mode? + + def show + expires_in 10.seconds, public: true if current_account.nil? + end + + private + + def set_generated_annual_report + @generated_annual_report = GeneratedAnnualReport.find_by!(account: @account, year: params[:year], share_key: params[:share_key]) + end +end diff --git a/app/lib/annual_report.rb b/app/lib/annual_report.rb index 6da9ecae14..12d69dfba8 100644 --- a/app/lib/annual_report.rb +++ b/app/lib/annual_report.rb @@ -43,7 +43,8 @@ class AnnualReport account: @account, year: @year, schema_version: SCHEMA, - data: data + data: data, + share_key: SecureRandom.hex(8) ) end diff --git a/app/models/generated_annual_report.rb b/app/models/generated_annual_report.rb index aba0712fe4..df78d4f5fe 100644 --- a/app/models/generated_annual_report.rb +++ b/app/models/generated_annual_report.rb @@ -5,13 +5,14 @@ # Table name: generated_annual_reports # # id :bigint(8) not null, primary key -# account_id :bigint(8) not null -# year :integer not null # data :jsonb not null # schema_version :integer not null +# share_key :string # viewed_at :datetime +# year :integer not null # created_at :datetime not null # updated_at :datetime not null +# account_id :bigint(8) not null # class GeneratedAnnualReport < ApplicationRecord diff --git a/app/serializers/rest/annual_report_serializer.rb b/app/serializers/rest/annual_report_serializer.rb index 1fb5ddb5c1..a7d611f8ef 100644 --- a/app/serializers/rest/annual_report_serializer.rb +++ b/app/serializers/rest/annual_report_serializer.rb @@ -1,5 +1,9 @@ # frozen_string_literal: true class REST::AnnualReportSerializer < ActiveModel::Serializer - attributes :year, :data, :schema_version + attributes :year, :data, :schema_version, :share_url + + def share_url + public_wrapstodon_url(object.account, object.year, object.share_key) if object.share_key.present? + end end diff --git a/app/views/wrapstodon/show.html.haml b/app/views/wrapstodon/show.html.haml new file mode 100644 index 0000000000..6978f231f9 --- /dev/null +++ b/app/views/wrapstodon/show.html.haml @@ -0,0 +1,9 @@ +- content_for :page_title, t('wrapstodon.title', name: display_name(@account), year: @generated_annual_report.year) + +- content_for :header_tags do + %meta{ name: 'robots', content: 'noindex, noarchive' }/ + + = opengraph 'og:site_name', site_title + = opengraph 'profile:username', acct(@account)[1..] + += render 'shared/web_app' diff --git a/config/locales/en.yml b/config/locales/en.yml index 62b8823d9f..0c44a7c3bf 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2186,3 +2186,5 @@ en: not_supported: This browser doesn't support security keys otp_required: To use security keys please enable two-factor authentication first. registered_on: Registered on %{date} + wrapstodon: + title: Wrapstodon %{year} for %{name} diff --git a/config/routes.rb b/config/routes.rb index 412372600e..3685e695f9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -163,6 +163,7 @@ Rails.application.routes.draw do get '/@:account_username/followers', to: 'follower_accounts#index' get '/@:account_username/:id', to: 'statuses#show', as: :short_account_status get '/@:account_username/:id/embed', to: 'statuses#embed', as: :embed_short_account_status + get '/@:account_username/wrapstodon/:year/:share_key', to: 'wrapstodon#show', as: :public_wrapstodon end get '/@:username_with_domain/(*any)', to: 'home#index', constraints: { username_with_domain: %r{([^/])+?} }, as: :account_with_domain, format: false diff --git a/db/migrate/20251202140424_add_share_key_to_generated_annual_reports.rb b/db/migrate/20251202140424_add_share_key_to_generated_annual_reports.rb new file mode 100644 index 0000000000..aa4d15a15a --- /dev/null +++ b/db/migrate/20251202140424_add_share_key_to_generated_annual_reports.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddShareKeyToGeneratedAnnualReports < ActiveRecord::Migration[8.0] + def change + add_column :generated_annual_reports, :share_key, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 669a6dbf2e..4e8a9f6efb 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[8.0].define(version: 2025_12_01_155054) do +ActiveRecord::Schema[8.0].define(version: 2025_12_02_140424) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" @@ -616,6 +616,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_12_01_155054) do t.datetime "viewed_at" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.string "share_key" t.index ["account_id", "year"], name: "index_generated_annual_reports_on_account_id_and_year", unique: true end