From 3b472e9f0436bacb4c833b4674187e0d12144687 Mon Sep 17 00:00:00 2001 From: noellabo Date: Wed, 15 Feb 2023 17:04:19 +0900 Subject: [PATCH] Add generator support --- app/controllers/generators_controller.rb | 21 +++++++++++++++ app/lib/activitypub/activity/create.rb | 12 +++++++++ app/models/generator.rb | 27 +++++++++++++++++++ app/models/status.rb | 3 +++ .../activitypub/generator_serializer.rb | 24 +++++++++++++++++ .../activitypub/note_serializer.rb | 6 +++++ app/serializers/rest/status_serializer.rb | 10 ++++++- config/routes.rb | 1 + .../20230215053626_create_generators.rb | 12 +++++++++ .../20230215062659_add_generator_to_status.rb | 5 ++++ db/schema.rb | 13 ++++++++- 11 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 app/controllers/generators_controller.rb create mode 100644 app/models/generator.rb create mode 100644 app/serializers/activitypub/generator_serializer.rb create mode 100644 db/migrate/20230215053626_create_generators.rb create mode 100644 db/migrate/20230215062659_add_generator_to_status.rb diff --git a/app/controllers/generators_controller.rb b/app/controllers/generators_controller.rb new file mode 100644 index 000000000..17d984d9d --- /dev/null +++ b/app/controllers/generators_controller.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class GeneratorsController < ApplicationController + before_action :set_generator + before_action :set_cache_headers + + def show + respond_to do |format| + format.json do + expires_in 3.minutes, public: true + render_with_cache json: @generator, content_type: 'application/activity+json', serializer: ActivityPub::GeneratorSerializer, adapter: ActivityPub::Adapter + end + end + end + + private + + def set_generator + @generator = Doorkeeper::Application.find(params[:id]) + end +end diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index 97950e500..172ccf6af 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -120,6 +120,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity media_attachment_ids: process_attachments.take(4).map(&:id), poll: process_poll, quote: quote, + generator: generator, } end end @@ -585,4 +586,15 @@ class ActivityPub::Activity::Create < ActivityPub::Activity rescue nil end + + def generator + @generator ||= Generator.find_or_create_by!( + uri: @object.dig('generator', 'id') || '', + type: @object.dig('generator', 'type')&.capitalize&.to_sym || :Application, + name: @object.dig('generator', 'name') || '', + website: @object.dig('generator', 'url') || '', + ) + rescue + nil + end end diff --git a/app/models/generator.rb b/app/models/generator.rb new file mode 100644 index 000000000..b7c26fa7b --- /dev/null +++ b/app/models/generator.rb @@ -0,0 +1,27 @@ +# == Schema Information +# +# Table name: generators +# +# id :bigint(8) not null, primary key +# uri :string default(""), not null +# type :integer default(:application), not null +# name :string default(""), not null +# website :string +# created_at :datetime not null +# updated_at :datetime not null +# +class Generator < ApplicationRecord + self.inheritance_column = false + + enum type: { Application: 0 }, _suffix: :type + + validate :validate_uri_unique + + has_many :statuses, dependent: :nullify, inverse_of: :generator + + private + + def validate_uri_unique + error.add(:base, :invalid) if uri.present? && Generator.where(uri: uri).exists? + end +end diff --git a/app/models/status.rb b/app/models/status.rb index 3d60b6260..6d6e4b7bc 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -26,6 +26,7 @@ # quote_id :bigint(8) # expired_at :datetime # searchability :integer +# generator_id :bigint(8) # class Status < ApplicationRecord @@ -57,6 +58,7 @@ class Status < ApplicationRecord UNCOUNT_VISIBILITY = %w(direct personal) belongs_to :application, class_name: 'Doorkeeper::Application', optional: true + belongs_to :generator, optional: true, inverse_of: :statuses belongs_to :account, inverse_of: :statuses belongs_to :in_reply_to_account, foreign_key: 'in_reply_to_account_id', class_name: 'Account', optional: true @@ -146,6 +148,7 @@ class Status < ApplicationRecord :tags, :preview_cards, :preloadable_poll, + :generator, references: { account: :account_stat }, account: [:account_stat, :user], active_mentions: { account: :account_stat }, diff --git a/app/serializers/activitypub/generator_serializer.rb b/app/serializers/activitypub/generator_serializer.rb new file mode 100644 index 000000000..289edfd3d --- /dev/null +++ b/app/serializers/activitypub/generator_serializer.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +class ActivityPub::GeneratorSerializer < ActiveModel::Serializer + include RoutingHelper + + attributes :id, :type, :name, :url + + def id + generator_url(object) + end + + def type + 'Application' + end + + def name + object.name || '' + end + + def url + object.website || '' + end +end + diff --git a/app/serializers/activitypub/note_serializer.rb b/app/serializers/activitypub/note_serializer.rb index 540a00bea..5c1cc2743 100644 --- a/app/serializers/activitypub/note_serializer.rb +++ b/app/serializers/activitypub/note_serializer.rb @@ -33,6 +33,8 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer attribute :voters_count, if: :poll_and_voters_count? + belongs_to :application, key: :generator, if: :show_application?, serializer: ActivityPub::GeneratorSerializer + def id ActivityPub::TagManager.instance.uri_for(object) end @@ -221,6 +223,10 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer object.preloadable_poll&.voters_count end + def show_application? + local? && object.account.user_shows_application? + end + class MediaAttachmentSerializer < ActivityPub::Serializer context_extensions :blurhash, :focal_point diff --git a/app/serializers/rest/status_serializer.rb b/app/serializers/rest/status_serializer.rb index 9b3f5aa2b..b97e23660 100644 --- a/app/serializers/rest/status_serializer.rb +++ b/app/serializers/rest/status_serializer.rb @@ -43,6 +43,10 @@ class REST::StatusSerializer < ActiveModel::Serializer delegate :quote?, to: :object + def application + object.account.local? ? object.application : object.generator + end + def preview_card object.preview_card unless hide_preview_card? end @@ -101,7 +105,7 @@ class REST::StatusSerializer < ActiveModel::Serializer end def show_application? - object.account.user_shows_application? || owned_status? + !object.account.local? || object.account.user_shows_application? || owned_status? end def expires? @@ -251,6 +255,10 @@ class REST::StatusSerializer < ActiveModel::Serializer attributes :name, :website end + class GeneratorSerializer < ActiveModel::Serializer + attributes :name, :website + end + class MentionSerializer < ActiveModel::Serializer attributes :id, :username, :url, :acct, :group diff --git a/config/routes.rb b/config/routes.rb index f6f249ee3..f5082a560 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -189,6 +189,7 @@ Rails.application.routes.draw do resources :emoji_reactions, only: [:show] resources :invites, only: [:index, :create, :destroy] resources :filters, except: [:show] + resources :generators, only: [:show] resource :relationships, only: [:show, :update] resource :statuses_cleanup, controller: :statuses_cleanup, only: [:show, :update] diff --git a/db/migrate/20230215053626_create_generators.rb b/db/migrate/20230215053626_create_generators.rb new file mode 100644 index 000000000..35584d348 --- /dev/null +++ b/db/migrate/20230215053626_create_generators.rb @@ -0,0 +1,12 @@ +class CreateGenerators < ActiveRecord::Migration[6.1] + def change + create_table :generators do |t| + t.string :uri, null: false, default: '' + t.integer :type, null: false, default: 0 + t.string :name, null: false, default: '' + t.string :website, null: true + + t.timestamps + end + end +end diff --git a/db/migrate/20230215062659_add_generator_to_status.rb b/db/migrate/20230215062659_add_generator_to_status.rb new file mode 100644 index 000000000..bb8113f8c --- /dev/null +++ b/db/migrate/20230215062659_add_generator_to_status.rb @@ -0,0 +1,5 @@ +class AddGeneratorToStatus < ActiveRecord::Migration[6.1] + def change + safety_assured { add_reference :statuses, :generator, null: true, default: nil, foreign_key: { on_delete: :cascade }, index: false } + end +end diff --git a/db/schema.rb b/db/schema.rb index abb3882bc..e19670b44 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.define(version: 2023_01_29_193248) do +ActiveRecord::Schema.define(version: 2023_02_15_062659) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -549,6 +549,15 @@ ActiveRecord::Schema.define(version: 2023_01_29_193248) do t.index ["target_account_id"], name: "index_follows_on_target_account_id" end + create_table "generators", force: :cascade do |t| + t.string "uri", default: "", null: false + t.integer "type", default: 0, null: false + t.string "name", default: "", null: false + t.string "website" + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + end + create_table "identities", force: :cascade do |t| t.string "provider", default: "", null: false t.string "uid", default: "", null: false @@ -1003,6 +1012,7 @@ ActiveRecord::Schema.define(version: 2023_01_29_193248) do t.bigint "quote_id" t.datetime "expired_at" t.integer "searchability" + t.bigint "generator_id" t.index ["account_id", "id", "visibility", "updated_at"], name: "index_statuses_20210710", order: { id: :desc }, where: "((deleted_at IS NULL) AND (expired_at IS NULL))" t.index ["account_id", "id"], name: "index_statuses_private_searchable", order: { id: :desc }, where: "((deleted_at IS NULL) AND (expired_at IS NULL) AND (reblog_of_id IS NULL) AND (searchability = ANY (ARRAY[0, 1, 2])))" 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)))" @@ -1264,6 +1274,7 @@ ActiveRecord::Schema.define(version: 2023_01_29_193248) do add_foreign_key "status_stats", "statuses", on_delete: :cascade add_foreign_key "statuses", "accounts", column: "in_reply_to_account_id", name: "fk_c7fa917661", on_delete: :nullify add_foreign_key "statuses", "accounts", name: "fk_9bda1543f7", on_delete: :cascade + add_foreign_key "statuses", "generators", on_delete: :cascade add_foreign_key "statuses", "statuses", column: "in_reply_to_id", on_delete: :nullify add_foreign_key "statuses", "statuses", column: "reblog_of_id", on_delete: :cascade add_foreign_key "statuses_tags", "statuses", on_delete: :cascade