From 44ef583a23dcc2ac29b728ef7990c605bf1c1355 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 6 Mar 2024 14:12:04 -0300 Subject: [PATCH 001/133] fix: send es un nombre reservado --- app/jobs/activity_pub/remote_flag_job.rb | 2 +- app/models/activity_pub/remote_flag.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/jobs/activity_pub/remote_flag_job.rb b/app/jobs/activity_pub/remote_flag_job.rb index 7d8131db..26db937a 100644 --- a/app/jobs/activity_pub/remote_flag_job.rb +++ b/app/jobs/activity_pub/remote_flag_job.rb @@ -22,7 +22,7 @@ class ActivityPub raise 'No se pudo enviar el reporte' unless response.ok? - remote_flag.send! + remote_flag.report! rescue Exception => e ExceptionNotifier.notify_exception(e, data: { remote_flag: remote_flag.id, response: response.parsed_response }) raise diff --git a/app/models/activity_pub/remote_flag.rb b/app/models/activity_pub/remote_flag.rb index 25f1b743..76143414 100644 --- a/app/models/activity_pub/remote_flag.rb +++ b/app/models/activity_pub/remote_flag.rb @@ -14,7 +14,7 @@ class ActivityPub transitions from: :waiting, to: :queued end - event :send do + event :report do transitions from: :queued, to: :sent end From 19d998086c0f7e1d2813b68f3f8d4c31c16d75d3 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 6 Mar 2024 14:31:32 -0300 Subject: [PATCH 002/133] fix: raise --- app/models/activity_pub.rb | 6 ++---- app/models/actor_moderation.rb | 6 +++--- app/models/fediblock_state.rb | 4 ++-- app/models/instance_moderation.rb | 6 +++--- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/app/models/activity_pub.rb b/app/models/activity_pub.rb index b07fe790..3887d512 100644 --- a/app/models/activity_pub.rb +++ b/app/models/activity_pub.rb @@ -68,8 +68,7 @@ class ActivityPub < ApplicationRecord transitions from: %i[paused], to: :approved before do - raise AASM::InvalidTransition unless - site.social_inbox.inbox.accept(id: object.uri).ok? + raise unless site.social_inbox.inbox.accept(id: object.uri).ok? end end @@ -78,8 +77,7 @@ class ActivityPub < ApplicationRecord transitions from: %i[paused approved], to: :rejected before do - raise AASM::InvalidTransition unless - site.social_inbox.inbox.reject(id: object.uri).ok? + raise unless site.social_inbox.inbox.reject(id: object.uri).ok? end end diff --git a/app/models/actor_moderation.rb b/app/models/actor_moderation.rb index d7eea709..5d44a021 100644 --- a/app/models/actor_moderation.rb +++ b/app/models/actor_moderation.rb @@ -65,21 +65,21 @@ class ActorModeration < ApplicationRecord end def pause_remotely! - raise AASM::InvalidTransition unless + raise unless actor.mention && site.social_inbox.allowlist.delete(list: [actor.mention]).ok? && site.social_inbox.blocklist.delete(list: [actor.mention]).ok? end def allow_remotely! - raise AASM::InvalidTransition unless + raise unless actor.mention && site.social_inbox.allowlist.post(list: [actor.mention]).ok? && site.social_inbox.blocklist.delete(list: [actor.mention]).ok? end def block_remotely! - raise AASM::InvalidTransition unless + raise unless actor.mention && site.social_inbox.allowlist.delete(list: [actor.mention]).ok? && site.social_inbox.blocklist.post(list: [actor.mention]).ok? diff --git a/app/models/fediblock_state.rb b/app/models/fediblock_state.rb index 180a45b5..214e2f5e 100644 --- a/app/models/fediblock_state.rb +++ b/app/models/fediblock_state.rb @@ -89,14 +89,14 @@ class FediblockState < ApplicationRecord # Al deshabilitar, las instancias pasan a ser analizadas caso por caso def disable_remotely! - raise AASM::InvalidTransition unless + raise unless site.social_inbox.blocklist.delete(list: list_names).ok? && site.social_inbox.allowlist.delete(list: list_names).ok? end # Al habilitar, se bloquean todas las instancias de la lista def enable_remotely! - raise AASM::InvalidTransition unless + raise unless site.social_inbox.blocklist.post(list: list_names).ok? && site.social_inbox.allowlist.delete(list: list_names).ok? end diff --git a/app/models/instance_moderation.rb b/app/models/instance_moderation.rb index 7447cc89..ef04b7ff 100644 --- a/app/models/instance_moderation.rb +++ b/app/models/instance_moderation.rb @@ -60,7 +60,7 @@ class InstanceModeration < ApplicationRecord # # @return [Boolean] def pause_remotely! - raise AASM::InvalidTransition unless + raise unless site.social_inbox.blocklist.delete(list: [instance.list_name]).ok? && site.social_inbox.allowlist.delete(list: [instance.list_name]).ok? end @@ -69,7 +69,7 @@ class InstanceModeration < ApplicationRecord # # @return [Boolean] def block_remotely! - raise AASM::InvalidTransition unless + raise unless site.social_inbox.allowlist.delete(list: [instance.list_name]).ok? && site.social_inbox.blocklist.post(list: [instance.list_name]).ok? end @@ -78,7 +78,7 @@ class InstanceModeration < ApplicationRecord # # @return [Boolean] def allow_remotely! - raise AASM::InvalidTransition unless + raise unless site.social_inbox.blocklist.delete(list: [instance.list_name]).ok? && site.social_inbox.allowlist.post(list: [instance.list_name]).ok? end From fbd741960bd178b473d194980f16e63f1e190e8f Mon Sep 17 00:00:00 2001 From: f Date: Wed, 6 Mar 2024 14:42:08 -0300 Subject: [PATCH 003/133] =?UTF-8?q?fix:=20crear=20hooks=20y=20blocklists?= =?UTF-8?q?=20despu=C3=A9s=20de=20publicar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/deploy_social_distributed_press.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/models/deploy_social_distributed_press.rb b/app/models/deploy_social_distributed_press.rb index eec8189b..b7525dca 100644 --- a/app/models/deploy_social_distributed_press.rb +++ b/app/models/deploy_social_distributed_press.rb @@ -7,9 +7,6 @@ class DeploySocialDistributedPress < Deploy # Solo luego de publicar remotamente DEPENDENCIES = %i[deploy_distributed_press deploy_rsync deploy_full_rsync].freeze - after_save :create_hooks! - after_create :enable_fediblocks! - # Envía las notificaciones def deploy(output: false) with_tempfile(site.private_key_pem) do |file| @@ -18,6 +15,10 @@ class DeploySocialDistributedPress < Deploy run %(bundle exec jekyll notify --trace --key #{key} --destination "#{dest}"), output: output end + + + create_hooks! + enable_fediblocks! end # Igual que DeployLocal From ea34b2c676e079948cbf51a05a039d9383f2f828 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 6 Mar 2024 15:03:17 -0300 Subject: [PATCH 004/133] fix: hostnames --- app/models/fediblock_state.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/fediblock_state.rb b/app/models/fediblock_state.rb index 214e2f5e..b5258fb6 100644 --- a/app/models/fediblock_state.rb +++ b/app/models/fediblock_state.rb @@ -82,8 +82,8 @@ class FediblockState < ApplicationRecord # @return [Array] def list_names - @list_names ||= fediblock.instances.map do |instance| - "@*@#{instance}" + @list_names ||= fediblock.hostnames.map do |hostname| + "@*@#{hostname}" end end From 38ba8795debf571c1e6173116f56d4c594f2ba7d Mon Sep 17 00:00:00 2001 From: f Date: Wed, 6 Mar 2024 15:36:00 -0300 Subject: [PATCH 005/133] fix: el reporte remoto es opcional --- app/models/activity_pub.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/activity_pub.rb b/app/models/activity_pub.rb index 3887d512..33cd4d45 100644 --- a/app/models/activity_pub.rb +++ b/app/models/activity_pub.rb @@ -18,7 +18,7 @@ class ActivityPub < ApplicationRecord belongs_to :site belongs_to :object, polymorphic: true belongs_to :actor - belongs_to :remote_flag, class_name: 'ActivityPub::RemoteFlag' + belongs_to :remote_flag, optional: true, class_name: 'ActivityPub::RemoteFlag' has_many :activities validates :site_id, presence: true From b334d49654a295b439de085ef0cd0352c7f47645 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 6 Mar 2024 15:41:46 -0300 Subject: [PATCH 006/133] fix: actualizar al estado remoto --- app/controllers/api/v1/webhooks/social_inbox_controller.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/v1/webhooks/social_inbox_controller.rb b/app/controllers/api/v1/webhooks/social_inbox_controller.rb index 548781fa..c40857e0 100644 --- a/app/controllers/api/v1/webhooks/social_inbox_controller.rb +++ b/app/controllers/api/v1/webhooks/social_inbox_controller.rb @@ -53,7 +53,8 @@ module Api instance.present? object.present? activity.present? - activity_pub.approve! if activity_pub.may_approve? + activity_pub.update(aasm_state: 'approved') + activity.update_activity_pub_state! end head :accepted @@ -69,7 +70,7 @@ module Api instance.present? object.present? activity.present? - activity_pub.reject! if activity_pub.may_reject? + activity_pub.update(aasm_state: 'rejected') end head :accepted From 5c22015fe2d7ca3c418005ba4e8a989560270640 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 6 Mar 2024 15:47:27 -0300 Subject: [PATCH 007/133] fix: el reporte remoto es opcional --- app/models/actor_moderation.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/actor_moderation.rb b/app/models/actor_moderation.rb index 5d44a021..e06ffbb1 100644 --- a/app/models/actor_moderation.rb +++ b/app/models/actor_moderation.rb @@ -9,7 +9,7 @@ class ActorModeration < ApplicationRecord IGNORED_STATES = [] belongs_to :site - belongs_to :remote_flag, class_name: 'ActivityPub::RemoteFlag' + belongs_to :remote_flag, optional: true, class_name: 'ActivityPub::RemoteFlag' belongs_to :actor, class_name: 'ActivityPub::Actor' accepts_nested_attributes_for :remote_flag From 019101ba1e07448eceec8e390070f632420d72bc Mon Sep 17 00:00:00 2001 From: f Date: Wed, 6 Mar 2024 15:54:47 -0300 Subject: [PATCH 008/133] feat: almacenar el perfil de le actore en la base de datos --- app/jobs/activity_pub/actor_fetch_job.rb | 2 +- app/models/activity_pub/actor.rb | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/app/jobs/activity_pub/actor_fetch_job.rb b/app/jobs/activity_pub/actor_fetch_job.rb index 71107151..1190e936 100644 --- a/app/jobs/activity_pub/actor_fetch_job.rb +++ b/app/jobs/activity_pub/actor_fetch_job.rb @@ -19,7 +19,7 @@ class ActivityPub return unless response.ok? return if response.miss? && actor.content.present? - actor.update(content: FastJsonparser.parse(response.body)) + actor.object.update(content: FastJsonparser.parse(response.body)) end end end diff --git a/app/models/activity_pub/actor.rb b/app/models/activity_pub/actor.rb index fe6052bf..0a39dcde 100644 --- a/app/models/activity_pub/actor.rb +++ b/app/models/activity_pub/actor.rb @@ -25,5 +25,13 @@ class ActivityPub @mention ||= "@#{content['preferredUsername']}@#{instance.hostname}" end + + def object + @object ||= ActivityPub::Object::Person.find_or_initialize_by(uri: uri) + end + + def content + object.content + end end end From 5f5b929aa89098726ddb55098756d01e060fd9dc Mon Sep 17 00:00:00 2001 From: f Date: Wed, 6 Mar 2024 15:59:48 -0300 Subject: [PATCH 009/133] fix: traducciones --- config/locales/en.yml | 17 ++++++++--------- config/locales/es.yml | 17 ++++++++--------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/config/locales/en.yml b/config/locales/en.yml index 0ca90aa7..0f010c89 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -50,6 +50,14 @@ en: pm: pm format: '%-I:%M %p' components: + actor: + user: Username + profile: Profile + profile_name: Profile name + preferred_name: Name in Fediverse + profile_id: ID + profile_published: Published + profile_summary: Summary block_list: know_more: Know more instances_blocked: Instances blocked @@ -107,15 +115,6 @@ en: text_allow: Always approve text_block: Block text_report: Report - actor_moderations: - show: - user: Username - profile: Profile - profile_name: Profile name - preferred_name: Name in Fediverse - profile_id: ID - profile_published: Published - profile_summary: Summary remote_flags: report_message: "Hi! Someone using Sutty CMS reported this account on your instance. We don't have support for customized report messages yet, but we will soon. You can reach us at %{panel_actor_mention}." moderation_queue: diff --git a/config/locales/es.yml b/config/locales/es.yml index 40a1a18c..0a2538a7 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -50,6 +50,14 @@ es: pm: pm format: '%-H:%M' components: + actor: + user: Nombre de usuarie + profile: Cuenta de Origen + profile_name: Nombre de la cuenta + preferred_name: Nombre en el Fediverso + profile_id: ID + profile_published: Publicada + profile_summary: Presentación block_list: know_more: Saber más (en inglés) instances_blocked: Instancias bloqueadas @@ -106,15 +114,6 @@ es: text_allow: Aprobar siempre text_block: Bloquear text_report: Reportar - actor_moderations: - show: - user: Nombre de usuarie - profile: Cuenta de Origen - profile_name: Nombre de la cuenta - preferred_name: Nombre en el Fediverso - profile_id: ID - profile_published: Publicada - profile_summary: Presentación remote_flags: report_message: "¡Hola! Une usuarie de Sutty CMS reportó esta cuenta en tu instancia. Todavía no tenemos soporte para mensajes personalizados. Podés contactarnos en %{panel_actor_mention}." moderation_queue: From 2e04bc8eac09bcfc4929ca9670971fcf5da35056 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 6 Mar 2024 17:15:21 -0300 Subject: [PATCH 010/133] =?UTF-8?q?fix:=20las=20actividades=20est=C3=A1n?= =?UTF-8?q?=20duplicadas=20con=20respecto=20a=20su=20estado?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit por ejemplo, el borrado de une actore puede estar dirigido a todos los sitios, con lo que se crea varias veces (aunque se ejecuta solo una) --- app/models/activity_pub/activity.rb | 2 ++ app/models/activity_pub/actor.rb | 3 +++ app/models/activity_pub/concerns/json_ld_concern.rb | 2 -- app/models/activity_pub/object.rb | 3 +++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/models/activity_pub/activity.rb b/app/models/activity_pub/activity.rb index 1147c5b8..ee555474 100644 --- a/app/models/activity_pub/activity.rb +++ b/app/models/activity_pub/activity.rb @@ -20,6 +20,8 @@ class ActivityPub has_one :object, through: :activity_pub validates :activity_pub_id, presence: true + # Las actividades son únicas con respecto a su estado + validates :uri, presence: true, uniqueness: { scope: :activity_pub_id, message: 'estado duplicado' } # Siempre en orden descendiente para saber el último estado default_scope -> { order(created_at: :desc) } diff --git a/app/models/activity_pub/actor.rb b/app/models/activity_pub/actor.rb index 0a39dcde..919bc5e0 100644 --- a/app/models/activity_pub/actor.rb +++ b/app/models/activity_pub/actor.rb @@ -15,6 +15,9 @@ class ActivityPub has_many :activities has_many :remote_flags + # Les actores son únicxs a toda la base de datos + validates :uri, presence: true, uniqueness: true + # Obtiene el nombre de la Actor como mención, solo si obtuvimos el # contenido de antemano. # diff --git a/app/models/activity_pub/concerns/json_ld_concern.rb b/app/models/activity_pub/concerns/json_ld_concern.rb index bc30330c..282027df 100644 --- a/app/models/activity_pub/concerns/json_ld_concern.rb +++ b/app/models/activity_pub/concerns/json_ld_concern.rb @@ -6,8 +6,6 @@ class ActivityPub extend ActiveSupport::Concern included do - validates :uri, presence: true, uniqueness: true - # Cuando asignamos contenido, obtener la URI si no lo hicimos ya before_save :uri_from_content!, unless: :uri? diff --git a/app/models/activity_pub/object.rb b/app/models/activity_pub/object.rb index c196160f..15b07bee 100644 --- a/app/models/activity_pub/object.rb +++ b/app/models/activity_pub/object.rb @@ -5,6 +5,9 @@ class ActivityPub class Object < ApplicationRecord include ActivityPub::Concerns::JsonLdConcern + # Los objetos son únicos a toda la base de datos + validates :uri, presence: true, uniqueness: true + has_many :activity_pubs, as: :object # Encontrar le Actor por su relación con el objeto From 00f865f31542a15496355ea878954656323941f4 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 6 Mar 2024 17:22:01 -0300 Subject: [PATCH 011/133] fix: comentario en perfil de actore --- app/models/activity_pub/activity/delete.rb | 10 ++++++++-- app/views/moderation_queue/_comments.haml | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/models/activity_pub/activity/delete.rb b/app/models/activity_pub/activity/delete.rb index f6ff6536..5de20478 100644 --- a/app/models/activity_pub/activity/delete.rb +++ b/app/models/activity_pub/activity/delete.rb @@ -14,9 +14,15 @@ class ActivityPub # @see {https://docs.joinmastodon.org/spec/security/#ld} def update_activity_pub_state! ActivityPub.transaction do - ActivityPub::Object.find_by(uri: ActivityPub.uri_from_object(content['object']))&.activity_pubs&.find_each(&:remove!) + object = ActivityPub::Object.find_by(uri: ActivityPub.uri_from_object(content['object'])) - activity_pub.remove! + if object + object.activity_pubs.find_each do |activity_pub| + activity_pub.remove! if activity_pub.may_remove? + end + end + + activity_pub.remove! if activity_pub.may_remove? end end end diff --git a/app/views/moderation_queue/_comments.haml b/app/views/moderation_queue/_comments.haml index 436777db..316b097f 100644 --- a/app/views/moderation_queue/_comments.haml +++ b/app/views/moderation_queue/_comments.haml @@ -14,4 +14,4 @@ - moderation_queue.each do |activity_pub| -# cache [activity_pub, activity_pub.object, activity_pub.actor] do %hr - = render 'comment', comment: activity_pub.object.content, profile: activity_pub.actor.content, activity_pub: activity_pub, form_id: form_id + = render 'moderation_queue/comment', comment: activity_pub.object.content, profile: activity_pub.actor.content, activity_pub: activity_pub, form_id: form_id From 7aa14bd292bafbee9fd0a4dea874ae639e0a9737 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 6 Mar 2024 17:36:13 -0300 Subject: [PATCH 012/133] fix: poder ir al perfil --- app/views/components/_actor.haml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/views/components/_actor.haml b/app/views/components/_actor.haml index 3983d617..c58beae0 100644 --- a/app/views/components/_actor.haml +++ b/app/views/components/_actor.haml @@ -1,5 +1,7 @@ -# Componente Remote_Profile +- uri = text_plain(remote_profile['id']) + .py-2 %dl %dt= t('.profile_name') @@ -10,7 +12,7 @@ %dt= t('.profile_id') %dd - = link_to text_plain(remote_profile['id']) + = link_to uri, uri - if remote_profile['published'].present? %dt= t('.profile_published') From a8f184ecbf80b91336a7ee6a2ce5d4177f4e044e Mon Sep 17 00:00:00 2001 From: f Date: Wed, 6 Mar 2024 17:45:21 -0300 Subject: [PATCH 013/133] feat: validar que las uris sean uris --- app/models/activity_pub/activity.rb | 2 +- app/models/activity_pub/actor.rb | 2 +- app/models/activity_pub/object.rb | 2 +- app/validators/url_validator.rb | 21 +++++++++++++++++++++ 4 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 app/validators/url_validator.rb diff --git a/app/models/activity_pub/activity.rb b/app/models/activity_pub/activity.rb index ee555474..af005ff3 100644 --- a/app/models/activity_pub/activity.rb +++ b/app/models/activity_pub/activity.rb @@ -21,7 +21,7 @@ class ActivityPub validates :activity_pub_id, presence: true # Las actividades son únicas con respecto a su estado - validates :uri, presence: true, uniqueness: { scope: :activity_pub_id, message: 'estado duplicado' } + validates :uri, presence: true, url: true, uniqueness: { scope: :activity_pub_id, message: 'estado duplicado' } # Siempre en orden descendiente para saber el último estado default_scope -> { order(created_at: :desc) } diff --git a/app/models/activity_pub/actor.rb b/app/models/activity_pub/actor.rb index 919bc5e0..b03145e7 100644 --- a/app/models/activity_pub/actor.rb +++ b/app/models/activity_pub/actor.rb @@ -16,7 +16,7 @@ class ActivityPub has_many :remote_flags # Les actores son únicxs a toda la base de datos - validates :uri, presence: true, uniqueness: true + validates :uri, presence: true, url: true, uniqueness: true # Obtiene el nombre de la Actor como mención, solo si obtuvimos el # contenido de antemano. diff --git a/app/models/activity_pub/object.rb b/app/models/activity_pub/object.rb index 15b07bee..3fde326b 100644 --- a/app/models/activity_pub/object.rb +++ b/app/models/activity_pub/object.rb @@ -6,7 +6,7 @@ class ActivityPub include ActivityPub::Concerns::JsonLdConcern # Los objetos son únicos a toda la base de datos - validates :uri, presence: true, uniqueness: true + validates :uri, presence: true, url: true, uniqueness: true has_many :activity_pubs, as: :object diff --git a/app/validators/url_validator.rb b/app/validators/url_validator.rb new file mode 100644 index 00000000..291f9288 --- /dev/null +++ b/app/validators/url_validator.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +# Valida URLs +# +# @see {https://storck.io/posts/better-http-url-validation-in-ruby-on-rails/} +class UrlValidator < ActiveModel::EachValidator + def validate_each(record, attribute, value) + if value.blank? + record.errors.add(attribute, :url_missing) + return + end + + uri = URI.parse(value) + + record.errors.add(attribute, :scheme_missing) if uri.scheme.blank? + record.errors.add(attribute, :host_missing) if uri.host.blank? + record.errors.add(attribute, :path_missing) if uri.path.blank? + rescue URI::Error + record.errors.add(attribute, :invalid) + end +end From 66e59ee5ad8143630b07965560c3951d5cb2cac2 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 7 Mar 2024 16:54:09 -0300 Subject: [PATCH 014/133] feat: algunos tipos son actores --- app/models/activity_pub/object.rb | 8 ++++++ app/models/activity_pub/object/application.rb | 4 ++- app/models/activity_pub/object/audio.rb | 10 +++++++ .../object/concerns/actor_type_concern.rb | 27 +++++++++++++++++++ app/models/activity_pub/object/document.rb | 10 +++++++ app/models/activity_pub/object/event.rb | 10 +++++++ app/models/activity_pub/object/group.rb | 10 +++++++ app/models/activity_pub/object/image.rb | 10 +++++++ .../activity_pub/object/organization.rb | 4 ++- app/models/activity_pub/object/page.rb | 10 +++++++ app/models/activity_pub/object/person.rb | 4 ++- app/models/activity_pub/object/place.rb | 10 +++++++ app/models/activity_pub/object/profile.rb | 10 +++++++ .../activity_pub/object/relationship.rb | 10 +++++++ app/models/activity_pub/object/service.rb | 10 +++++++ app/models/activity_pub/object/tombstone.rb | 10 +++++++ app/models/activity_pub/object/video.rb | 10 +++++++ 17 files changed, 164 insertions(+), 3 deletions(-) create mode 100644 app/models/activity_pub/object/audio.rb create mode 100644 app/models/activity_pub/object/concerns/actor_type_concern.rb create mode 100644 app/models/activity_pub/object/document.rb create mode 100644 app/models/activity_pub/object/event.rb create mode 100644 app/models/activity_pub/object/group.rb create mode 100644 app/models/activity_pub/object/image.rb create mode 100644 app/models/activity_pub/object/page.rb create mode 100644 app/models/activity_pub/object/place.rb create mode 100644 app/models/activity_pub/object/profile.rb create mode 100644 app/models/activity_pub/object/relationship.rb create mode 100644 app/models/activity_pub/object/service.rb create mode 100644 app/models/activity_pub/object/tombstone.rb create mode 100644 app/models/activity_pub/object/video.rb diff --git a/app/models/activity_pub/object.rb b/app/models/activity_pub/object.rb index 3fde326b..b33c1957 100644 --- a/app/models/activity_pub/object.rb +++ b/app/models/activity_pub/object.rb @@ -16,5 +16,13 @@ class ActivityPub def actor ActivityPub::Actor.find_by(uri: content['actor']) end + + def actor_type? + false + end + + def object_type? + true + end end end diff --git a/app/models/activity_pub/object/application.rb b/app/models/activity_pub/object/application.rb index 99ac935c..d26a7757 100644 --- a/app/models/activity_pub/object/application.rb +++ b/app/models/activity_pub/object/application.rb @@ -5,6 +5,8 @@ # Una aplicación o instancia class ActivityPub class Object - class Application < ActivityPub::Object; end + class Application < ActivityPub::Object + include Concerns::ActorTypeConcern + end end end diff --git a/app/models/activity_pub/object/audio.rb b/app/models/activity_pub/object/audio.rb new file mode 100644 index 00000000..48caea44 --- /dev/null +++ b/app/models/activity_pub/object/audio.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# = Audio = +# +# Representa artículos +class ActivityPub + class Object + class Audio < ActivityPub::Object; end + end +end diff --git a/app/models/activity_pub/object/concerns/actor_type_concern.rb b/app/models/activity_pub/object/concerns/actor_type_concern.rb new file mode 100644 index 00000000..bb840601 --- /dev/null +++ b/app/models/activity_pub/object/concerns/actor_type_concern.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +class ActivityPub + class Object + module Concerns + module ActorTypeConcern + extend ActiveSupport::Concern + + included do + # El objeto referencia a une Actor + # + # @see {https://www.w3.org/TR/activitystreams-vocabulary/#actor-types} + def actor_type? + true + end + + # El objeto es un objeto + # + # @see {https://www.w3.org/TR/activitystreams-vocabulary/#object-types} + def object_type? + false + end + end + end + end + end +end diff --git a/app/models/activity_pub/object/document.rb b/app/models/activity_pub/object/document.rb new file mode 100644 index 00000000..d7444514 --- /dev/null +++ b/app/models/activity_pub/object/document.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# = Document = +# +# Representa artículos +class ActivityPub + class Object + class Document < ActivityPub::Object; end + end +end diff --git a/app/models/activity_pub/object/event.rb b/app/models/activity_pub/object/event.rb new file mode 100644 index 00000000..9fa1f6fc --- /dev/null +++ b/app/models/activity_pub/object/event.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# = Event = +# +# Representa artículos +class ActivityPub + class Object + class Event < ActivityPub::Object; end + end +end diff --git a/app/models/activity_pub/object/group.rb b/app/models/activity_pub/object/group.rb new file mode 100644 index 00000000..08d11d0d --- /dev/null +++ b/app/models/activity_pub/object/group.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# = Group = +class ActivityPub + class Object + class Group < ActivityPub::Object + include Concerns::ActorTypeConcern + end + end +end diff --git a/app/models/activity_pub/object/image.rb b/app/models/activity_pub/object/image.rb new file mode 100644 index 00000000..9939a14b --- /dev/null +++ b/app/models/activity_pub/object/image.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# = Image = +# +# Representa artículos +class ActivityPub + class Object + class Image < ActivityPub::Object; end + end +end diff --git a/app/models/activity_pub/object/organization.rb b/app/models/activity_pub/object/organization.rb index e3385232..e820c305 100644 --- a/app/models/activity_pub/object/organization.rb +++ b/app/models/activity_pub/object/organization.rb @@ -5,6 +5,8 @@ # Una organización class ActivityPub class Object - class Organization < ActivityPub::Object; end + class Organization < ActivityPub::Object + include Concerns::ActorTypeConcern + end end end diff --git a/app/models/activity_pub/object/page.rb b/app/models/activity_pub/object/page.rb new file mode 100644 index 00000000..f05503e2 --- /dev/null +++ b/app/models/activity_pub/object/page.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# = Page = +# +# Representa artículos +class ActivityPub + class Object + class Page < ActivityPub::Object; end + end +end diff --git a/app/models/activity_pub/object/person.rb b/app/models/activity_pub/object/person.rb index a6a85d43..5bcab596 100644 --- a/app/models/activity_pub/object/person.rb +++ b/app/models/activity_pub/object/person.rb @@ -5,6 +5,8 @@ # Una persona, el perfil de une actore class ActivityPub class Object - class Person < ActivityPub::Object; end + class Person < ActivityPub::Object + include Concerns::ActorTypeConcern + end end end diff --git a/app/models/activity_pub/object/place.rb b/app/models/activity_pub/object/place.rb new file mode 100644 index 00000000..f04032ed --- /dev/null +++ b/app/models/activity_pub/object/place.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# = Place = +# +# Representa artículos +class ActivityPub + class Object + class Place < ActivityPub::Object; end + end +end diff --git a/app/models/activity_pub/object/profile.rb b/app/models/activity_pub/object/profile.rb new file mode 100644 index 00000000..8f7183a2 --- /dev/null +++ b/app/models/activity_pub/object/profile.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# = Profile = +# +# Representa artículos +class ActivityPub + class Object + class Profile < ActivityPub::Object; end + end +end diff --git a/app/models/activity_pub/object/relationship.rb b/app/models/activity_pub/object/relationship.rb new file mode 100644 index 00000000..ece995b4 --- /dev/null +++ b/app/models/activity_pub/object/relationship.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# = Relationship = +# +# Representa artículos +class ActivityPub + class Object + class Relationship < ActivityPub::Object; end + end +end diff --git a/app/models/activity_pub/object/service.rb b/app/models/activity_pub/object/service.rb new file mode 100644 index 00000000..a276ea5b --- /dev/null +++ b/app/models/activity_pub/object/service.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# = Service = +class ActivityPub + class Object + class Service < ActivityPub::Object + include Concerns::ActorTypeConcern + end + end +end diff --git a/app/models/activity_pub/object/tombstone.rb b/app/models/activity_pub/object/tombstone.rb new file mode 100644 index 00000000..88f136b9 --- /dev/null +++ b/app/models/activity_pub/object/tombstone.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# = Tombstone = +# +# Representa artículos +class ActivityPub + class Object + class Tombstone < ActivityPub::Object; end + end +end diff --git a/app/models/activity_pub/object/video.rb b/app/models/activity_pub/object/video.rb new file mode 100644 index 00000000..fa4bbffb --- /dev/null +++ b/app/models/activity_pub/object/video.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# = Video = +# +# Representa artículos +class ActivityPub + class Object + class Video < ActivityPub::Object; end + end +end From 5a7331e00e44074bd9639d9023f707721665655b Mon Sep 17 00:00:00 2001 From: f Date: Thu, 7 Mar 2024 17:17:58 -0300 Subject: [PATCH 015/133] =?UTF-8?q?feat:=20cuando=20une=20actore=20es=20el?= =?UTF-8?q?iminade,=20hay=20que=20eliminar=20sus=20estados=20de=20moderaci?= =?UTF-8?q?=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/activity_pub/activity/delete.rb | 8 +++++++- app/models/activity_pub/object.rb | 7 ++++++- .../object/concerns/actor_type_concern.rb | 7 +++++++ app/models/actor_moderation.rb | 15 +++++++++++++-- .../20240307201510_remove_actor_moderations.rb | 13 +++++++++++++ 5 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 db/migrate/20240307201510_remove_actor_moderations.rb diff --git a/app/models/activity_pub/activity/delete.rb b/app/models/activity_pub/activity/delete.rb index 5de20478..6a23a8b5 100644 --- a/app/models/activity_pub/activity/delete.rb +++ b/app/models/activity_pub/activity/delete.rb @@ -16,10 +16,16 @@ class ActivityPub ActivityPub.transaction do object = ActivityPub::Object.find_by(uri: ActivityPub.uri_from_object(content['object'])) - if object + if object.present? object.activity_pubs.find_each do |activity_pub| activity_pub.remove! if activity_pub.may_remove? end + + # Encontrar todas las acciones de moderación de le actore + # eliminade y moverlas a eliminar. + if object.actor_type? && object.actor.present? + ActorModeration.where(actor_id: object.actor.id).remove_all! + end end activity_pub.remove! if activity_pub.may_remove? diff --git a/app/models/activity_pub/object.rb b/app/models/activity_pub/object.rb index b33c1957..9061c4c5 100644 --- a/app/models/activity_pub/object.rb +++ b/app/models/activity_pub/object.rb @@ -14,7 +14,12 @@ class ActivityPub # # @return [ActivityPub::Actor,nil] def actor - ActivityPub::Actor.find_by(uri: content['actor']) + ActivityPub::Actor.find_by(uri: actor_uri) + end + + # @return [String] + def actor_uri + content['attributedTo'] end def actor_type? diff --git a/app/models/activity_pub/object/concerns/actor_type_concern.rb b/app/models/activity_pub/object/concerns/actor_type_concern.rb index bb840601..b2a643c7 100644 --- a/app/models/activity_pub/object/concerns/actor_type_concern.rb +++ b/app/models/activity_pub/object/concerns/actor_type_concern.rb @@ -7,6 +7,13 @@ class ActivityPub extend ActiveSupport::Concern included do + # La URI de le Actor en este caso es la misma id + # + # @return [String] + def actor_uri + uri + end + # El objeto referencia a une Actor # # @see {https://www.w3.org/TR/activitystreams-vocabulary/#actor-types} diff --git a/app/models/actor_moderation.rb b/app/models/actor_moderation.rb index e06ffbb1..01613f72 100644 --- a/app/models/actor_moderation.rb +++ b/app/models/actor_moderation.rb @@ -5,8 +5,8 @@ class ActorModeration < ApplicationRecord include AASM include AasmEventsConcern - IGNORED_EVENTS = [] - IGNORED_STATES = [] + IGNORED_EVENTS = %i[remove] + IGNORED_STATES = %i[removed] belongs_to :site belongs_to :remote_flag, optional: true, class_name: 'ActivityPub::RemoteFlag' @@ -23,11 +23,16 @@ class ActorModeration < ApplicationRecord self.update_all(aasm_state: 'paused', updated_at: Time.now) end + def self.remove_all! + self.update_all(aasm_state: 'removed', updated_at: Time.now) + end + aasm do state :paused, initial: true state :allowed state :blocked state :reported + state :removed event :pause do transitions from: %i[allowed blocked reported], to: :paused @@ -62,6 +67,12 @@ class ActorModeration < ApplicationRecord ActivityPub::RemoteFlagJob.perform_later(remote_flag: remote_flag) if remote_flag.waiting? end end + + # Si un perfil es eliminado remotamente, tenemos que dejar de + # mostrarlo y todas sus actividades. + event :remove do + transitions to: :removed + end end def pause_remotely! diff --git a/db/migrate/20240307201510_remove_actor_moderations.rb b/db/migrate/20240307201510_remove_actor_moderations.rb new file mode 100644 index 00000000..92c6d23a --- /dev/null +++ b/db/migrate/20240307201510_remove_actor_moderations.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +# Mover todes les actores eliminades +class RemoveActorModerations < ActiveRecord::Migration[6.1] + def up + actor_ids = + ActivityPub.where(aasm_state: 'removed', object_type: 'ActivityPub::Object::Person').distinct.pluck(:actor_id) + + ActorModeration.where(id: actor_ids).remove_all! + end + + def down; end +end From b0b8e6877efee116e707efea7ea05145387a7c00 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 7 Mar 2024 17:21:03 -0300 Subject: [PATCH 016/133] =?UTF-8?q?fixup!=20feat:=20cuando=20une=20actore?= =?UTF-8?q?=20es=20eliminade,=20hay=20que=20eliminar=20sus=20estados=20de?= =?UTF-8?q?=20moderaci=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db/migrate/20240307201510_remove_actor_moderations.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrate/20240307201510_remove_actor_moderations.rb b/db/migrate/20240307201510_remove_actor_moderations.rb index 92c6d23a..b451c589 100644 --- a/db/migrate/20240307201510_remove_actor_moderations.rb +++ b/db/migrate/20240307201510_remove_actor_moderations.rb @@ -6,7 +6,7 @@ class RemoveActorModerations < ActiveRecord::Migration[6.1] actor_ids = ActivityPub.where(aasm_state: 'removed', object_type: 'ActivityPub::Object::Person').distinct.pluck(:actor_id) - ActorModeration.where(id: actor_ids).remove_all! + ActorModeration.where(actor_id: actor_ids).remove_all! end def down; end From 2370cf73108b315c833f370f876209896efe7067 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 7 Mar 2024 17:44:40 -0300 Subject: [PATCH 017/133] =?UTF-8?q?feat:=20eliminar=20m=C3=A1s=20actores?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../20240307203039_remove_actor_moderations2.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 db/migrate/20240307203039_remove_actor_moderations2.rb diff --git a/db/migrate/20240307203039_remove_actor_moderations2.rb b/db/migrate/20240307203039_remove_actor_moderations2.rb new file mode 100644 index 00000000..dabc7ed7 --- /dev/null +++ b/db/migrate/20240307203039_remove_actor_moderations2.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +# Algunes quedaron como genéricxs +class RemoveActorModerations2 < ActiveRecord::Migration[6.1] + def up + actor_uris = ActivityPub::Activity.where(type: 'ActivityPub::Activity::Delete').distinct.pluck(Arel.sql("content->>'object'")) + actor_ids = ActivityPub::Actor.where(uri: actor_uris).ids + + ActorModeration.where(actor_id: actor_ids).remove_all! + end + + def down; end +end From 21401d93b6c79a34a55e8d3d8c97cd029c96428a Mon Sep 17 00:00:00 2001 From: f Date: Thu, 7 Mar 2024 17:46:32 -0300 Subject: [PATCH 018/133] =?UTF-8?q?fixup!=20feat:=20eliminar=20m=C3=A1s=20?= =?UTF-8?q?actores?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db/migrate/20240307203039_remove_actor_moderations2.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrate/20240307203039_remove_actor_moderations2.rb b/db/migrate/20240307203039_remove_actor_moderations2.rb index dabc7ed7..555a4ffe 100644 --- a/db/migrate/20240307203039_remove_actor_moderations2.rb +++ b/db/migrate/20240307203039_remove_actor_moderations2.rb @@ -3,7 +3,7 @@ # Algunes quedaron como genéricxs class RemoveActorModerations2 < ActiveRecord::Migration[6.1] def up - actor_uris = ActivityPub::Activity.where(type: 'ActivityPub::Activity::Delete').distinct.pluck(Arel.sql("content->>'object'")) + actor_uris = ActivityPub::Activity.unscope(:order).where(type: 'ActivityPub::Activity::Delete').distinct.pluck(Arel.sql("content->>'object'")) actor_ids = ActivityPub::Actor.where(uri: actor_uris).ids ActorModeration.where(actor_id: actor_ids).remove_all! From 550dba08c5cafc9ce9aeee120177fdb0b904be62 Mon Sep 17 00:00:00 2001 From: f Date: Fri, 8 Mar 2024 11:24:57 -0300 Subject: [PATCH 019/133] fix: migraciones --- db/structure.sql | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/db/structure.sql b/db/structure.sql index 97bd372e..ed58ebec 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -2699,6 +2699,8 @@ INSERT INTO "schema_migrations" (version) VALUES ('20240301194154'), ('20240301202955'), ('20240305164653'), -('20240305184854'); +('20240305184854'), +('20240307201510'), +('20240307203039'); From fc77f8e9f406c854155f966e2f7b8fc869e6218c Mon Sep 17 00:00:00 2001 From: f Date: Fri, 8 Mar 2024 13:08:24 -0300 Subject: [PATCH 020/133] fix: form es opcional #15329 --- app/views/components/_checkbox.haml | 3 ++- app/views/components/_comments_checked_submenu.haml | 5 ++++- app/views/components/_comments_filters.haml | 5 ++++- app/views/components/_dropdown_button.haml | 2 +- app/views/components/_instances_checked_submenu.haml | 5 ++++- app/views/components/_instances_filters.haml | 5 ++++- app/views/components/_profiles_checked_submenu.haml | 5 ++++- app/views/components/_profiles_filters.haml | 5 ++++- app/views/components/_select_all.haml | 2 +- app/views/components/_select_all_container.haml | 4 ++-- app/views/moderation_queue/_account.haml | 5 ++++- app/views/moderation_queue/_accounts.haml | 8 ++++---- app/views/moderation_queue/_comment.haml | 3 ++- app/views/moderation_queue/_comments.haml | 8 ++++---- app/views/moderation_queue/_instance.haml | 2 +- app/views/moderation_queue/_instances.haml | 8 ++++---- 16 files changed, 49 insertions(+), 26 deletions(-) diff --git a/app/views/components/_checkbox.haml b/app/views/components/_checkbox.haml index 68f1a663..a58c85b7 100644 --- a/app/views/components/_checkbox.haml +++ b/app/views/components/_checkbox.haml @@ -1,5 +1,6 @@ -# Componente Checkbox - local_assigns[:name] ||= id + .custom-control.custom-checkbox - %input.custom-control-input{ form: local_assigns[:form_id], type: 'checkbox', id: id, **local_assigns } + %input.custom-control-input{ type: 'checkbox', id: id, **local_assigns.compact } %label.custom-control-label{ for: id }= yield diff --git a/app/views/components/_comments_checked_submenu.haml b/app/views/components/_comments_checked_submenu.haml index a09da426..d94e12a9 100644 --- a/app/views/components/_comments_checked_submenu.haml +++ b/app/views/components/_comments_checked_submenu.haml @@ -1,6 +1,9 @@ +-# + @param form [String] + - current_state = params[:activity_pub_state]&.to_sym || ActivityPub.states.first - ActivityPub.aasm.events.each do |event| - next if ActivityPub::IGNORED_EVENTS.include? event.name - next unless event.transitions_from_state?(current_state) - = render 'components/dropdown_button', form_id: form_id, text: t(".submenu_#{event.name}"), name: 'activity_pub_action', value: event.name + = render 'components/dropdown_button', form: form, text: t(".submenu_#{event.name}"), name: 'activity_pub_action', value: event.name diff --git a/app/views/components/_comments_filters.haml b/app/views/components/_comments_filters.haml index 35cd5dda..cf8c1aa2 100644 --- a/app/views/components/_comments_filters.haml +++ b/app/views/components/_comments_filters.haml @@ -1,9 +1,12 @@ +-# + @params form [String] + - current_state = params[:activity_pub_state]&.to_sym || ActivityPub.states.first .d-flex.py-2 - if ActivityPub.transitionable_events(current_state).present? = render 'components/dropdown', text: t('.text_checked') do - = render 'components/comments_checked_submenu', form_id: form_id + = render 'components/comments_checked_submenu', form: form = render 'components/dropdown', text: t('.text_show') do = render 'components/comments_show_submenu', activity_pubs: activity_pubs diff --git a/app/views/components/_dropdown_button.haml b/app/views/components/_dropdown_button.haml index c8c98209..c0f12754 100644 --- a/app/views/components/_dropdown_button.haml +++ b/app/views/components/_dropdown_button.haml @@ -1,4 +1,4 @@ -# @param name [String] @param value [String] -%button.dropdown-item{type: 'submit', data: { target: 'dropdown.item' }, name: name, value: value, form: local_assigns[:form_id] }= text +%button.dropdown-item{type: 'submit', data: { target: 'dropdown.item' }, name: name, value: value, **local_assigns.compact } diff --git a/app/views/components/_instances_checked_submenu.haml b/app/views/components/_instances_checked_submenu.haml index 4c45b7ab..7c9dbd87 100644 --- a/app/views/components/_instances_checked_submenu.haml +++ b/app/views/components/_instances_checked_submenu.haml @@ -1,2 +1,5 @@ +-# + @params form [String] + - InstanceModeration.transitionable_events(current_state).each do |event| - = render 'components/dropdown_button', text: t(".submenu_#{event}"), name: 'instance_moderation_action', value: event, form_id: form_id + = render 'components/dropdown_button', text: t(".submenu_#{event}"), name: 'instance_moderation_action', value: event, form: form diff --git a/app/views/components/_instances_filters.haml b/app/views/components/_instances_filters.haml index 2c23fd72..730184bd 100644 --- a/app/views/components/_instances_filters.haml +++ b/app/views/components/_instances_filters.haml @@ -1,9 +1,12 @@ +-# + @params form [String] + - current_state = params[:state]&.to_sym || InstanceModeration.states.first .d-flex.py-2 - if InstanceModeration.transitionable_events(current_state).present? = render 'components/dropdown', text: t('.text_checked') do - = render 'components/instances_checked_submenu', form_id: form_id, current_state: current_state + = render 'components/instances_checked_submenu', form: form, current_state: current_state = render 'components/dropdown', text: t('.text_show') do = render 'components/instances_show_submenu', instance_moderations: instance_moderations diff --git a/app/views/components/_profiles_checked_submenu.haml b/app/views/components/_profiles_checked_submenu.haml index 66a0fa78..04c86fd4 100644 --- a/app/views/components/_profiles_checked_submenu.haml +++ b/app/views/components/_profiles_checked_submenu.haml @@ -1,2 +1,5 @@ +-# + @params form [String] + - ActorModeration.transitionable_events(current_state).each do |actor_event| - = render 'components/dropdown_button', text: t(".submenu_#{actor_event}"), name: 'actor_moderation_action', value: actor_event, form_id: form_id + = render 'components/dropdown_button', text: t(".submenu_#{actor_event}"), name: 'actor_moderation_action', value: actor_event, form: form diff --git a/app/views/components/_profiles_filters.haml b/app/views/components/_profiles_filters.haml index bf7fb48a..3f830ec8 100644 --- a/app/views/components/_profiles_filters.haml +++ b/app/views/components/_profiles_filters.haml @@ -1,9 +1,12 @@ +-# + @params form [String] + - current_state = params[:actor_state]&.to_sym || ActorModeration.states.first .d-flex.py-2 - if ActorModeration.transitionable_events(current_state).present? = render 'components/dropdown', text: t('.text_checked') do - = render 'components/profiles_checked_submenu', form_id: form_id, current_state: current_state + = render 'components/profiles_checked_submenu', form: form, current_state: current_state = render 'components/dropdown', text: t('.text_show') do = render 'components/profiles_show_submenu', actor_moderations: actor_moderations diff --git a/app/views/components/_select_all.haml b/app/views/components/_select_all.haml index 68711c4a..9778cd13 100644 --- a/app/views/components/_select_all.haml +++ b/app/views/components/_select_all.haml @@ -1,4 +1,4 @@ -# @param id [String] -= render 'components/checkbox', id: id, form: local_assigns[:form_id], data: { action: 'select-all#toggle', target: 'select-all.toggle' } do += render 'components/checkbox', id: id, data: { action: 'select-all#toggle', target: 'select-all.toggle', **local_assigns.compact } do %span.sr-only= t('.label') diff --git a/app/views/components/_select_all_container.haml b/app/views/components/_select_all_container.haml index 5fa91e2d..8c8d9426 100644 --- a/app/views/components/_select_all_container.haml +++ b/app/views/components/_select_all_container.haml @@ -7,7 +7,7 @@ navegador los va a asignar a este formulario. @param path [String] - @param form_id [String] + @param form [String] -= form_tag path, id: form_id, method: :patch do += form_tag path, id: form, method: :patch do -# nada diff --git a/app/views/moderation_queue/_account.haml b/app/views/moderation_queue/_account.haml index f63b6f6f..6b4c67fc 100644 --- a/app/views/moderation_queue/_account.haml +++ b/app/views/moderation_queue/_account.haml @@ -1,6 +1,9 @@ +-# + @params form [String] + .row.no-gutters.pt-2 .col-1 - = render 'components/checkbox', id: actor_moderation.id, form_id: form_id, name: 'actor_moderation[]', value: actor_moderation.id, data: { target: 'select-all.input' } + = render 'components/checkbox', id: actor_moderation.id, form: form, name: 'actor_moderation[]', value: actor_moderation.id, data: { target: 'select-all.input' } .col-11 %h4 = link_to text_plain(profile['name']), site_actor_moderation_path(id: actor_moderation) diff --git a/app/views/moderation_queue/_accounts.haml b/app/views/moderation_queue/_accounts.haml index 65ff953f..abc02b31 100644 --- a/app/views/moderation_queue/_accounts.haml +++ b/app/views/moderation_queue/_accounts.haml @@ -1,17 +1,17 @@ - form_id = 'actor_moderations_action_on_several' -= render 'components/select_all_container', path: site_actor_moderations_action_on_several_path, form_id: form_id += render 'components/select_all_container', path: site_actor_moderations_action_on_several_path, form: form_id .row.no-gutters.pt-2{ data: { controller: 'select-all' } } .col-1.d-flex.align-items-center - = render 'components/select_all', id: 'actors', form_id: form_id + = render 'components/select_all', id: 'actors', form: form_id .col-11 -# Filtros - = render 'components/profiles_filters', actor_moderations: actor_moderations, form_id: form_id + = render 'components/profiles_filters', actor_moderations: actor_moderations, form: form_id .col-12 - if actor_moderations.count.zero? %h4= t('moderation_queue.nothing') - actor_moderations.find_each do |actor_moderation| - cache [actor_moderation, actor_moderation.actor] do %hr - = render 'account', actor_moderation: actor_moderation, profile: actor_moderation.actor.content, form_id: form_id + = render 'account', actor_moderation: actor_moderation, profile: actor_moderation.actor.content, form: form_id diff --git a/app/views/moderation_queue/_comment.haml b/app/views/moderation_queue/_comment.haml index 33ebc722..90579a9c 100644 --- a/app/views/moderation_queue/_comment.haml +++ b/app/views/moderation_queue/_comment.haml @@ -1,6 +1,7 @@ -# Componente Comentario + @param form [String] @param profile [Hash] @param comment [Hash] @param activity_pub [ActivityPub] @@ -10,7 +11,7 @@ .row.no-gutters .col-1 - = render 'components/checkbox', id: activity_pub.id, name: 'activity_pub[]', value: activity_pub.id, data: { target: 'select-all.input' }, form: form_id + = render 'components/checkbox', id: activity_pub.id, name: 'activity_pub[]', value: activity_pub.id, data: { target: 'select-all.input' }, form: form .col-11 .d-flex.flex-row.align-items-center.justify-content-between %h4.mb-0 diff --git a/app/views/moderation_queue/_comments.haml b/app/views/moderation_queue/_comments.haml index 316b097f..72240287 100644 --- a/app/views/moderation_queue/_comments.haml +++ b/app/views/moderation_queue/_comments.haml @@ -1,17 +1,17 @@ - form_id = 'activity_pub_action_on_several' -= render 'components/select_all_container', path: site_activity_pubs_action_on_several_path, form_id: form_id += render 'components/select_all_container', path: site_activity_pubs_action_on_several_path, form: form_id .row.no-gutters.pt-2{ data: { controller: 'select-all' } } .col-1.d-flex.align-items-center - = render 'components/select_all', id: 'select-all-comments', form_id: form_id + = render 'components/select_all', id: 'select-all-comments', form: form_id .col-11 -# Filtros - = render 'components/comments_filters', activity_pubs: moderation_queue, form_id: form_id + = render 'components/comments_filters', activity_pubs: moderation_queue, form: form_id .col-12 - if moderation_queue.count.zero? %h4= t('moderation_queue.nothing') - moderation_queue.each do |activity_pub| -# cache [activity_pub, activity_pub.object, activity_pub.actor] do %hr - = render 'moderation_queue/comment', comment: activity_pub.object.content, profile: activity_pub.actor.content, activity_pub: activity_pub, form_id: form_id + = render 'moderation_queue/comment', comment: activity_pub.object.content, profile: activity_pub.actor.content, activity_pub: activity_pub, form: form_id diff --git a/app/views/moderation_queue/_instance.haml b/app/views/moderation_queue/_instance.haml index 05510724..7cf3b085 100644 --- a/app/views/moderation_queue/_instance.haml +++ b/app/views/moderation_queue/_instance.haml @@ -3,7 +3,7 @@ .row.no-gutters.pt-2 .col-1 - = render 'components/checkbox', id: instance.hostname, form_id: form_id, name: 'instance_moderation[]', value: instance_moderation.id, data: { target: 'select-all.input' } + = render 'components/checkbox', id: instance.hostname, form: form, name: 'instance_moderation[]', value: instance_moderation.id, data: { target: 'select-all.input' } .col-11 %h4 %a{ href: instance.uri }= sanitize(instance.content['title']) || instance.hostname diff --git a/app/views/moderation_queue/_instances.haml b/app/views/moderation_queue/_instances.haml index 3954ce65..9a5349ba 100644 --- a/app/views/moderation_queue/_instances.haml +++ b/app/views/moderation_queue/_instances.haml @@ -1,13 +1,13 @@ - form_id = 'instance_moderation_action_on_several' %section - = render 'components/select_all_container', path: site_instance_moderations_action_on_several_path, form_id: form_id + = render 'components/select_all_container', path: site_instance_moderations_action_on_several_path, form: form_id .row.no-gutters.pt-2{ data: { controller: 'select-all' } } .col-1.d-flex.align-items-center - = render 'components/select_all', id: 'instances', form_id: form_id + = render 'components/select_all', id: 'instances', form: form_id .col-11 -# Filtros - = render 'components/instances_filters', instance_moderations: instance_moderations, form_id: form_id + = render 'components/instances_filters', instance_moderations: instance_moderations, form: form_id .col-12 - if instance_moderations.count.zero? @@ -16,7 +16,7 @@ - instance_moderations.each do |instance_moderation| - cache [instance_moderation.aasm_state, instance_moderation.instance] do %hr - = render 'moderation_queue/instance', instance_moderation: instance_moderation, instance: instance_moderation.instance, form_id: form_id + = render 'moderation_queue/instance', instance_moderation: instance_moderation, instance: instance_moderation.instance, form: form_id %hr %div From 4dff3180306ee4014e58aee69452b53bb0d9fc47 Mon Sep 17 00:00:00 2001 From: f Date: Fri, 8 Mar 2024 14:10:21 -0300 Subject: [PATCH 021/133] feat: al desactivar un fediblock, ignorar otros fediblocks --- app/models/fediblock_state.rb | 76 ++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 27 deletions(-) diff --git a/app/models/fediblock_state.rb b/app/models/fediblock_state.rb index b5258fb6..e50abaef 100644 --- a/app/models/fediblock_state.rb +++ b/app/models/fediblock_state.rb @@ -27,12 +27,14 @@ class FediblockState < ApplicationRecord transitions from: :disabled, to: :enabled before do - enable_remotely! + # Bloquear todos las instancias de este Fediblock + enable_remotely! list_names(fediblock.hostnames) # Al actualizar el estado en masa garantizamos que las # instancias que ya existen queden sincronizadas con el bloqueo # en masa que acabamos de hacer. - instance_moderations.block_all! + instance_ids = fediblock.instances.ids + site.instance_moderations.where(instance_id: instance_ids).block_all! # Luego esta tarea crea las que falten e ignora las que ya se # bloquearon. @@ -41,11 +43,13 @@ class FediblockState < ApplicationRecord # Bloquear a todes les Actores de las instancias bloqueadas para # indicarle a le usuarie que les tiene que desbloquear # manualmente. + actor_ids = ActivityPub::Actor.where(instance_id: instance_ids).ids ActorModeration.where(actor_id: actor_ids).paused.block_all! end end - # Al deshabilitar, las listas pasan a modo pausa. + # Al deshabilitar, las listas pasan a modo pausa, a menos que estén + # activas en otros listados. # # @todo No cambiar el estado si se habían habilitado manualmente, # pero esto implica que tenemos que encontrar las que sí y quitarlas @@ -54,12 +58,19 @@ class FediblockState < ApplicationRecord transitions from: :enabled, to: :disabled before do - disable_remotely! + # Deshabilitar todas las instancias que no estén habilitadas por + # otros fediblocks + disable_remotely! list_names(unique_hostnames) - instance_moderations.pause_all! + # Pausar todas las moderaciones de las instancias que no estén + # bloqueadas por otros fediblocks. + instance_ids = ActivityPub::Instance.where(hostname: unique_hostnames).ids + site.instance_moderations.where(instance_id: instance_ids).pause_all! # Volver a pausar todes les actores de esta instancia que fueron - # bloqueades. + # bloqueades, a menos que hayan sido bloqueades por otro + # fediblock. + actor_ids = ActivityPub::Actor.where(instance_id: instance_ids).ids ActorModeration.where(actor_id: actor_ids).blocked.pause_all! end end @@ -67,37 +78,48 @@ class FediblockState < ApplicationRecord private - def actor_ids - ActivityPub::Actor.where(instance_id: instance_ids).pluck(:id) - end - - def instance_ids - fediblock.instances.pluck(:id) - end - - # Todas las instancias de moderación de este sitio - def instance_moderations - site.instance_moderations.where(instance_id: instance_ids) - end - + # Devuelve los hostnames únicos a esta instancia. + # # @return [Array] - def list_names - @list_names ||= fediblock.hostnames.map do |hostname| + def unique_hostnames + @unique_hostnames ||= + begin + other_enabled_fediblock_ids = + site.fediblock_states.enabled.where.not(id: id).pluck(:fediblock_id) + other_enabled_hostnames = + ActivityPub::Fediblock + .where(id: other_enabled_fediblock_ids) + .pluck(:hostnames) + .flatten + .uniq + + fediblock.hostnames - other_enabled_hostnames + end + end + + # @param hostnames [Array] + # @return [Array] + def list_names(hostnames) + hostnames.map do |hostname| "@*@#{hostname}" end end # Al deshabilitar, las instancias pasan a ser analizadas caso por caso - def disable_remotely! + # + # @param list [Array] + def disable_remotely!(list) raise unless - site.social_inbox.blocklist.delete(list: list_names).ok? && - site.social_inbox.allowlist.delete(list: list_names).ok? + site.social_inbox.blocklist.delete(list: list).ok? && + site.social_inbox.allowlist.delete(list: list).ok? end # Al habilitar, se bloquean todas las instancias de la lista - def enable_remotely! + # + # @param list [Array] + def enable_remotely!(list) raise unless - site.social_inbox.blocklist.post(list: list_names).ok? && - site.social_inbox.allowlist.delete(list: list_names).ok? + site.social_inbox.blocklist.post(list: list).ok? && + site.social_inbox.allowlist.delete(list: list).ok? end end From 995c80fed1d3eb837122e1656a41261c544aaae3 Mon Sep 17 00:00:00 2001 From: f Date: Fri, 8 Mar 2024 14:42:27 -0300 Subject: [PATCH 022/133] fix: no colgar toda la cola si algo falla en la api --- app/controllers/fediblock_states_controller.rb | 13 ++++++++++++- app/jobs/activity_pub/instance_moderation_job.rb | 7 +++++++ config/locales/en.yml | 6 ++++++ config/locales/es.yml | 7 +++++++ 4 files changed, 32 insertions(+), 1 deletion(-) diff --git a/app/controllers/fediblock_states_controller.rb b/app/controllers/fediblock_states_controller.rb index 6d9737c3..4d9cc968 100644 --- a/app/controllers/fediblock_states_controller.rb +++ b/app/controllers/fediblock_states_controller.rb @@ -11,11 +11,22 @@ class FediblockStatesController < ApplicationController elsif fediblock_state.may_disable? fediblock_state.disable! end + + flash[:success] = I18n.t('fediblock_states.action_on_several.success') + rescue Exception => e + ExceptionNotifier.notify_exception(e, data: { site: site.name }) + + flash.delete(:success) + flash[:error] = I18n.t('fediblock_states.action_on_several.error') end # Bloquear otras instancias if custom_blocklist.present? - ActivityPub::InstanceModerationJob.perform_later(site: site, hostnames: custom_blocklist) + if ActivityPub::InstanceModerationJob.perform_now(site: site, hostnames: custom_blocklist) + flash[:success] = I18n.t('fediblock_states.action_on_several.custom_blocklist_success') + else + flash[:error] = I18n.t('fediblock_states.action_on_several.custom_blocklist_error') + end end redirect_to site_moderation_queue_path diff --git a/app/jobs/activity_pub/instance_moderation_job.rb b/app/jobs/activity_pub/instance_moderation_job.rb index b205e68f..17def46e 100644 --- a/app/jobs/activity_pub/instance_moderation_job.rb +++ b/app/jobs/activity_pub/instance_moderation_job.rb @@ -14,6 +14,7 @@ class ActivityPub end instances = ActivityPub::Instance.where(hostname: hostnames) + success = true Site.transaction do # Crea todas las moderaciones de instancia con un estado por @@ -23,9 +24,15 @@ class ActivityPub # idealmente son pocas instancias las que aparecen. site.instance_moderations.find_or_create_by(instance: instance).tap do |instance_moderation| instance_moderation.block! if instance_moderation.may_block? + # Notificar todos los errores sin detener la ejecución + rescue Exception => e + ExceptionNotifier.notify_exception(e, data: { site: site.name, instance_moderation: instance_moderation.id }) + success = false end end end + + success end end end diff --git a/config/locales/en.yml b/config/locales/en.yml index 0f010c89..565f0218 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -117,6 +117,12 @@ en: text_report: Report remote_flags: report_message: "Hi! Someone using Sutty CMS reported this account on your instance. We don't have support for customized report messages yet, but we will soon. You can reach us at %{panel_actor_mention}." + fediblock_states: + action_on_several: + success: "Blocklists have been enabled, you can find their instances by filtering by Blocked. Any pending account from these instances has also been blocked. You can approve them individually on the Accounts section." + error: "There was an error while enabling or disabling blocklists. We received a report and will be acting on it soon." + custom_blocklist_success: "Custom blocklist has been added, you can find the instances by filtering by Blocked. Any pending account from these instances has also been blocked. You can approve them individually on the Accounts section." + custom_blocklist_error: "There was an error while adding a custom blocklist. We received a report and will be acting on it soon." moderation_queue: everything: 'Select all' nothing: "There's nothing for this filter" diff --git a/config/locales/es.yml b/config/locales/es.yml index 0a2538a7..175ca661 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -116,6 +116,13 @@ es: text_report: Reportar remote_flags: report_message: "¡Hola! Une usuarie de Sutty CMS reportó esta cuenta en tu instancia. Todavía no tenemos soporte para mensajes personalizados. Podés contactarnos en %{panel_actor_mention}." + fediblock_states: + action_on_several: + success: "Se habilitaron las listas de bloqueo, podés encontrar las instancias filtrando por Bloqueadas. Todas las cuentas de estas instancias pendientes de moderación han sido bloqueadas. Podés activarlas individualmente en la sección Cuentas." + error: "Hubo un error al activar o desactivar listas de bloqueo, ya recibimos el reporte y lo estaremos verificando." + custom_blocklist_success: "Se agregaron las instancias personalizadas a la lista de bloqueo, podés encontrarlas filtrando por Bloqueadas. Todas las cuentas de estas instancias pendientes de moderación han sido bloqueadas. Podés aprobarlas individualmente en la sección Cuentas." + custom_blocklist_error: "Hubo un error al agregar instancias personalizadas a la lista de bloqueo, ya recibimos el reporte y lo estaremos verificando." + actor_moderations: moderation_queue: everything: 'Seleccionar todo' nothing: 'No hay nada para este filtro' From cb30a3d02c8b9f6e90faff000683951dd9e5c0ab Mon Sep 17 00:00:00 2001 From: f Date: Fri, 8 Mar 2024 15:07:32 -0300 Subject: [PATCH 023/133] fix: ser informatives --- app/controllers/actor_moderations_controller.rb | 16 +++++++++++++++- config/locales/en.yml | 16 ++++++++++++++++ config/locales/es.yml | 17 ++++++++++++++++- 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/app/controllers/actor_moderations_controller.rb b/app/controllers/actor_moderations_controller.rb index 6b924677..7c2c3d82 100644 --- a/app/controllers/actor_moderations_controller.rb +++ b/app/controllers/actor_moderations_controller.rb @@ -14,6 +14,12 @@ class ActorModerationsController < ApplicationController actor_moderation.public_send(:"#{actor_event}!") if actor_moderation.public_send(:"may_#{actor_event}?") + flash[:success] = I18n.t("actor_moderations.#{actor_event}.success") + rescue Exception => e + ExceptionNotifier.notify_exception(e, data: { site: site.name, params: params.permit!.to_h }) + + flash[:error] = I18n.t("actor_moderations.#{actor_event}.error") + ensure redirect_to_moderation_queue! end end @@ -21,7 +27,8 @@ class ActorModerationsController < ApplicationController # Ver el perfil remoto def show @remote_profile = actor_moderation.actor.content - @moderation_queue = rubanok_process(site.activity_pubs.where(actor_id: actor_moderation.actor_id), with: ActivityPubProcessor) + @moderation_queue = rubanok_process(site.activity_pubs.where(actor_id: actor_moderation.actor_id), + with: ActivityPubProcessor) end def action_on_several @@ -44,6 +51,13 @@ class ActorModerationsController < ApplicationController actor_moderation.update(actor_moderation_params(actor_moderation)) if action == :report actor_moderation.public_send(method) + + flash[:success] = I18n.t('actor_moderations.action_on_several.success') + rescue Exception => e + ExceptionNotifier.notify_exception(e, data: { site: site.name, params: params.permit!.to_h }) + + flash.delete(:success) + flash[:error] = I18n.t('actor_moderations.action_on_several.error') end end end diff --git a/config/locales/en.yml b/config/locales/en.yml index 565f0218..2ae8484b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -117,6 +117,22 @@ en: text_report: Report remote_flags: report_message: "Hi! Someone using Sutty CMS reported this account on your instance. We don't have support for customized report messages yet, but we will soon. You can reach us at %{panel_actor_mention}." + actor_moderations: + action_on_several: + success: "Several accounts have changed moderation state. You can find them using the filters on the Accounts section." + error: "There was an error while changing moderation state. We received a report and will be acting on it soon." + pause: + success: "Account paused. All of their comments will need to be moderated individually on the Comments section." + error: "There was an error while pausing the account. We received a report and will be acting on it soon." + allow: + success: "Account allowed. All of their comments will be approved automatically." + error: "There was an error while allowing the account. We received a report and will be acting on it soon." + block: + success: "Account blocked. All of their comments will be rejected automatically. If you want to report it anonymously to their instance, please use the Report button." + error: "There was an error while blocking the account. We received a report and will be acting on it soon." + report: + success: "Account reported." + error: "There was an error while reporting the account. We received a report and will be acting on it soon." fediblock_states: action_on_several: success: "Blocklists have been enabled, you can find their instances by filtering by Blocked. Any pending account from these instances has also been blocked. You can approve them individually on the Accounts section." diff --git a/config/locales/es.yml b/config/locales/es.yml index 175ca661..29815a61 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -116,13 +116,28 @@ es: text_report: Reportar remote_flags: report_message: "¡Hola! Une usuarie de Sutty CMS reportó esta cuenta en tu instancia. Todavía no tenemos soporte para mensajes personalizados. Podés contactarnos en %{panel_actor_mention}." + actor_moderations: + action_on_several: + success: "Se han modificado el estado de moderación de varias cuentas. Podés encontrarlas usando los filtros en la sección Cuentas." + error: "Hubo un error al modificar el estado de moderación de varias cuentas. Hemos recibido el reporte y lo estaremos verificando." + pause: + success: "Cuenta pausada. Todos los comentarios que haga necesitan ser aprobados manualmente en la sección Comentarios." + error: "No se pudo pausar la cuenta. Hemos recibido el reporte y lo estaremos verificando." + allow: + success: "Cuenta permitida. Todos los comentarios que haga serán aprobados inmediatamente." + error: "No se pudo permitir la cuenta. Hemos recibido el reporte y lo estaremos verificando." + block: + success: "Cuenta bloqueada. Todos los comentarios que haga serán rechazados inmediatamente. Si querés reportarla anónimamente a su instancia, podés usar el botón Reportar." + error: "No se pudo bloquear la cuenta. Hemos recibido el reporte y lo estaremos verificando." + report: + success: "Cuenta reportada a su instancia." + error: "No se pudo reportar la cuenta. Hemos recibido el reporte y lo estaremos verificando." fediblock_states: action_on_several: success: "Se habilitaron las listas de bloqueo, podés encontrar las instancias filtrando por Bloqueadas. Todas las cuentas de estas instancias pendientes de moderación han sido bloqueadas. Podés activarlas individualmente en la sección Cuentas." error: "Hubo un error al activar o desactivar listas de bloqueo, ya recibimos el reporte y lo estaremos verificando." custom_blocklist_success: "Se agregaron las instancias personalizadas a la lista de bloqueo, podés encontrarlas filtrando por Bloqueadas. Todas las cuentas de estas instancias pendientes de moderación han sido bloqueadas. Podés aprobarlas individualmente en la sección Cuentas." custom_blocklist_error: "Hubo un error al agregar instancias personalizadas a la lista de bloqueo, ya recibimos el reporte y lo estaremos verificando." - actor_moderations: moderation_queue: everything: 'Seleccionar todo' nothing: 'No hay nada para este filtro' From 8faa6d8ea8c7128f4b77987be117ddfde51d5d7e Mon Sep 17 00:00:00 2001 From: f Date: Fri, 8 Mar 2024 15:18:19 -0300 Subject: [PATCH 024/133] =?UTF-8?q?feat:=20ser=20m=C3=A1s=20informative=20?= =?UTF-8?q?con=20los=20comentarios?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/activity_pubs_controller.rb | 15 +++++++++++++++ config/locales/en.yml | 13 +++++++++++++ config/locales/es.yml | 13 +++++++++++++ 3 files changed, 41 insertions(+) diff --git a/app/controllers/activity_pubs_controller.rb b/app/controllers/activity_pubs_controller.rb index c8f86ef0..1efe2b89 100644 --- a/app/controllers/activity_pubs_controller.rb +++ b/app/controllers/activity_pubs_controller.rb @@ -11,6 +11,12 @@ class ActivityPubsController < ApplicationController activity_pub.update(remote_flag_params(activity_pub)) if event == :report activity_pub.public_send(:"#{event}!") if activity_pub.public_send(:"may_#{event}?") + flash[:success] = I18n.t("activity_pubs.#{event}.success") + rescue Exception => e + ExceptionNotifier.notify_exception(e, data: { site: site.name, params: params.permit!.to_h }) + + flash[:error] = I18n.t("activity_pubs.#{event}.error") + ensure redirect_to_moderation_queue! end end @@ -49,6 +55,15 @@ class ActivityPubsController < ApplicationController next unless activity_pub.public_send(may) activity_pub.public_send(method) + + flash[:success] = I18n.t('activity_pubs.action_on_several.success') + rescue Exception => e + ExceptionNotifier.notify_exception(e, + data: { site: site.name, params: params.permit!.to_h, + activity_pub: activity_pub.id }) + + flash.delete(:success) + flash[:error] = I18n.t('activity_pubs.action_on_several.error') end end end diff --git a/config/locales/en.yml b/config/locales/en.yml index 2ae8484b..23df9e3c 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -117,6 +117,19 @@ en: text_report: Report remote_flags: report_message: "Hi! Someone using Sutty CMS reported this account on your instance. We don't have support for customized report messages yet, but we will soon. You can reach us at %{panel_actor_mention}." + activity_pubs: + action_on_several: + success: "Several comments have changed moderation state. You can find them using the filters on the Comments section." + error: "There was an error while changing moderation state. We received a report and will be acting on it soon." + approve: + success: "Comment approved." + error: "There was an error while approving the comment. We received a report and will be acting on it soon." + reject: + success: "Comment rejected. You can report it using the Report button." + error: "There was an error while rejecting the comment. We received a report and will be acting on it soon." + report: + success: "Comment reported." + error: "There was an error while reporting the comment. We received a report and will be acting on it soon." actor_moderations: action_on_several: success: "Several accounts have changed moderation state. You can find them using the filters on the Accounts section." diff --git a/config/locales/es.yml b/config/locales/es.yml index 29815a61..1773a0c6 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -116,6 +116,19 @@ es: text_report: Reportar remote_flags: report_message: "¡Hola! Une usuarie de Sutty CMS reportó esta cuenta en tu instancia. Todavía no tenemos soporte para mensajes personalizados. Podés contactarnos en %{panel_actor_mention}." + activity_pubs: + action_on_several: + success: "Se ha modificado el estado de moderación de varios comentarios. Podés encontrarlos usando los filtros en la sección Comentarios." + error: "Hubo un error al modificar el estado de moderación de varios comentarios. Hemos recibido el reporte y lo estaremos verificando." + approve: + success: "Comentario aprobado." + error: "No se puedo aprobar el comentario. Hemos recibido el reporte y lo estaremos verificando." + reject: + success: "Comentario rechazado. Podés reportarlo usando el botón Reportar." + error: "No se puedo rechazar el comentario. Hemos recibido el reporte y lo estaremos verificando." + report: + success: "Comentario reportado." + error: "No se puedo reportar el comentario. Hemos recibido el reporte y lo estaremos verificando." actor_moderations: action_on_several: success: "Se han modificado el estado de moderación de varias cuentas. Podés encontrarlas usando los filtros en la sección Cuentas." From 42be495465a0fddd2c97db8e5df263f979a48957 Mon Sep 17 00:00:00 2001 From: f Date: Fri, 8 Mar 2024 15:19:21 -0300 Subject: [PATCH 025/133] =?UTF-8?q?fix:=20no=20prometer=20que=20el=20repor?= =?UTF-8?q?te=20es=20an=C3=B3nimo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit le admin de la instancia remota siempre puede d0xear --- config/locales/en.yml | 2 +- config/locales/es.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/locales/en.yml b/config/locales/en.yml index 23df9e3c..73920b57 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -141,7 +141,7 @@ en: success: "Account allowed. All of their comments will be approved automatically." error: "There was an error while allowing the account. We received a report and will be acting on it soon." block: - success: "Account blocked. All of their comments will be rejected automatically. If you want to report it anonymously to their instance, please use the Report button." + success: "Account blocked. All of their comments will be rejected automatically. If you want to report it to their instance, please use the Report button." error: "There was an error while blocking the account. We received a report and will be acting on it soon." report: success: "Account reported." diff --git a/config/locales/es.yml b/config/locales/es.yml index 1773a0c6..467f15e8 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -131,7 +131,7 @@ es: error: "No se puedo reportar el comentario. Hemos recibido el reporte y lo estaremos verificando." actor_moderations: action_on_several: - success: "Se han modificado el estado de moderación de varias cuentas. Podés encontrarlas usando los filtros en la sección Cuentas." + success: "Se ha modificado el estado de moderación de varias cuentas. Podés encontrarlas usando los filtros en la sección Cuentas." error: "Hubo un error al modificar el estado de moderación de varias cuentas. Hemos recibido el reporte y lo estaremos verificando." pause: success: "Cuenta pausada. Todos los comentarios que haga necesitan ser aprobados manualmente en la sección Comentarios." @@ -140,7 +140,7 @@ es: success: "Cuenta permitida. Todos los comentarios que haga serán aprobados inmediatamente." error: "No se pudo permitir la cuenta. Hemos recibido el reporte y lo estaremos verificando." block: - success: "Cuenta bloqueada. Todos los comentarios que haga serán rechazados inmediatamente. Si querés reportarla anónimamente a su instancia, podés usar el botón Reportar." + success: "Cuenta bloqueada. Todos los comentarios que haga serán rechazados inmediatamente. Si querés reportarla a su instancia, podés usar el botón Reportar." error: "No se pudo bloquear la cuenta. Hemos recibido el reporte y lo estaremos verificando." report: success: "Cuenta reportada a su instancia." From 6b74bc454da0a307c32c145af683297d2bc202eb Mon Sep 17 00:00:00 2001 From: f Date: Fri, 8 Mar 2024 15:20:08 -0300 Subject: [PATCH 026/133] fix: sin dummy data en posts --- app/controllers/posts_controller.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index 99dc6f7d..057c3068 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -38,7 +38,6 @@ class PostsController < ApplicationController @usuarie = site.usuarie? current_usuarie @site_stat = SiteStat.new(site) - dummy_data end def show @@ -82,7 +81,6 @@ class PostsController < ApplicationController authorize post breadcrumb post.title.value, site_post_path(site, post, locale: locale), match: :exact breadcrumb 'posts.edit', '' - dummy_data end def update From 071102fa3b82247fd7601f4548c3947106b6fd63 Mon Sep 17 00:00:00 2001 From: f Date: Fri, 8 Mar 2024 15:31:41 -0300 Subject: [PATCH 027/133] =?UTF-8?q?feat:=20al=20moderar=20una=20cuenta,=20?= =?UTF-8?q?tambi=C3=A9n=20moderar=20sus=20comentarios?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/actor_moderation.rb | 16 ++++++++++++++++ config/locales/en.yml | 4 ++-- config/locales/es.yml | 4 ++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/app/models/actor_moderation.rb b/app/models/actor_moderation.rb index 01613f72..4b220a58 100644 --- a/app/models/actor_moderation.rb +++ b/app/models/actor_moderation.rb @@ -42,19 +42,35 @@ class ActorModeration < ApplicationRecord end end + # Al permitir una cuenta se permiten todos los comentarios + # pendientes de moderación que ya hizo. event :allow do transitions from: %i[paused blocked reported], to: :allowed before do allow_remotely! + + site.activity_pubs.paused.where(actor_id: self.actor_id).find_each do |activity_pub| + activity_pub.allow! if activity_pub.may_allow? + rescue Exception => e + ExceptionNotifier.notify_exception(e, data: { site: site.name, activity_pub: activity_pub_id }) + end end end + # Al bloquear una cuenta se bloquean todos los comentarios + # pendientes de moderación que hizo. event :block do transitions from: %i[paused allowed], to: :blocked before do block_remotely! + + site.activity_pubs.paused.where(actor_id: self.actor_id).find_each do |activity_pub| + activity_pub.reject! if activity_pub.may_reject? + rescue Exception => e + ExceptionNotifier.notify_exception(e, data: { site: site.name, activity_pub: activity_pub_id }) + end end end diff --git a/config/locales/en.yml b/config/locales/en.yml index 73920b57..48d9ca61 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -138,10 +138,10 @@ en: success: "Account paused. All of their comments will need to be moderated individually on the Comments section." error: "There was an error while pausing the account. We received a report and will be acting on it soon." allow: - success: "Account allowed. All of their comments will be approved automatically." + success: "Account allowed. All of their comments will be approved automatically. Any pending comments have been approved." error: "There was an error while allowing the account. We received a report and will be acting on it soon." block: - success: "Account blocked. All of their comments will be rejected automatically. If you want to report it to their instance, please use the Report button." + success: "Account blocked. All of their comments will be rejected automatically. Any pending comments have been rejected. If you want to report it to their instance, please use the Report button." error: "There was an error while blocking the account. We received a report and will be acting on it soon." report: success: "Account reported." diff --git a/config/locales/es.yml b/config/locales/es.yml index 467f15e8..3df608f5 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -137,10 +137,10 @@ es: success: "Cuenta pausada. Todos los comentarios que haga necesitan ser aprobados manualmente en la sección Comentarios." error: "No se pudo pausar la cuenta. Hemos recibido el reporte y lo estaremos verificando." allow: - success: "Cuenta permitida. Todos los comentarios que haga serán aprobados inmediatamente." + success: "Cuenta permitida. Todos los comentarios que haga serán aprobados inmediatamente. Todos los comentarios pendientes de moderación fueron aprobados." error: "No se pudo permitir la cuenta. Hemos recibido el reporte y lo estaremos verificando." block: - success: "Cuenta bloqueada. Todos los comentarios que haga serán rechazados inmediatamente. Si querés reportarla a su instancia, podés usar el botón Reportar." + success: "Cuenta bloqueada. Todos los comentarios que haga serán rechazados inmediatamente. Todos los comentarios pendientes de moderación fueron rechazados. Si querés reportarla a su instancia, podés usar el botón Reportar." error: "No se pudo bloquear la cuenta. Hemos recibido el reporte y lo estaremos verificando." report: success: "Cuenta reportada a su instancia." From 522cef8c9ae99ce5cc3c2d0ae911e5611064a7d3 Mon Sep 17 00:00:00 2001 From: f Date: Fri, 8 Mar 2024 15:55:01 -0300 Subject: [PATCH 028/133] feat: acciones en masa para actividades --- app/models/activity_pub.rb | 34 ++++++++++++++++++++-- app/models/actor_moderation.rb | 12 ++------ app/models/concerns/aasm_events_concern.rb | 8 +++++ 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/app/models/activity_pub.rb b/app/models/activity_pub.rb index 33cd4d45..fce340db 100644 --- a/app/models/activity_pub.rb +++ b/app/models/activity_pub.rb @@ -38,6 +38,28 @@ class ActivityPub < ApplicationRecord end end + # Permite todos los comentarios. No podemos hacer acciones en masa + # sobre comentarios en la Social Inbox, con lo que tenemos que + # comunicarnos individualmente. + def self.allow_all! + self.find_each do |activity_pub| + activity_pub.allow! if activity_pub.may_allow? + rescue Exception => e + notify_exception!(e, activity_pub) + end + end + + # Rechaza todos los comentarios. No podemos hacer acciones en masa + # sobre comentarios en la Social Inbox, con lo que tenemos que + # comunicarnos individualmente. + def self.reject_all! + self.find_each do |activity_pub| + activity_pub.reject! if activity_pub.may_reject? + rescue Exception => e + notify_exception!(e, activity_pub) + end + end + aasm do # Todavía no hay una decisión sobre el objeto state :paused, initial: true @@ -68,7 +90,7 @@ class ActivityPub < ApplicationRecord transitions from: %i[paused], to: :approved before do - raise unless site.social_inbox.inbox.accept(id: object.uri).ok? + allow_remotely! end end @@ -77,7 +99,7 @@ class ActivityPub < ApplicationRecord transitions from: %i[paused approved], to: :rejected before do - raise unless site.social_inbox.inbox.reject(id: object.uri).ok? + reject_remotely! end end @@ -90,4 +112,12 @@ class ActivityPub < ApplicationRecord end end end + + def reject_remotely! + raise unless site.social_inbox.inbox.reject(id: object.uri).ok? + end + + def allow_remotely! + raise unless site.social_inbox.inbox.accept(id: object.uri).ok? + end end diff --git a/app/models/actor_moderation.rb b/app/models/actor_moderation.rb index 4b220a58..a510f1eb 100644 --- a/app/models/actor_moderation.rb +++ b/app/models/actor_moderation.rb @@ -50,11 +50,7 @@ class ActorModeration < ApplicationRecord before do allow_remotely! - site.activity_pubs.paused.where(actor_id: self.actor_id).find_each do |activity_pub| - activity_pub.allow! if activity_pub.may_allow? - rescue Exception => e - ExceptionNotifier.notify_exception(e, data: { site: site.name, activity_pub: activity_pub_id }) - end + site.activity_pubs.paused.where(actor_id: self.actor_id).allow_all! end end @@ -66,11 +62,7 @@ class ActorModeration < ApplicationRecord before do block_remotely! - site.activity_pubs.paused.where(actor_id: self.actor_id).find_each do |activity_pub| - activity_pub.reject! if activity_pub.may_reject? - rescue Exception => e - ExceptionNotifier.notify_exception(e, data: { site: site.name, activity_pub: activity_pub_id }) - end + site.activity_pubs.paused.where(actor_id: self.actor_id).reject_all! end end diff --git a/app/models/concerns/aasm_events_concern.rb b/app/models/concerns/aasm_events_concern.rb index 418368d8..b189dbe4 100644 --- a/app/models/concerns/aasm_events_concern.rb +++ b/app/models/concerns/aasm_events_concern.rb @@ -27,5 +27,13 @@ module AasmEventsConcern def self.states aasm.states.map(&:name) - self::IGNORED_STATES end + + # Envía notificación de errores + # + # @param exception [Exception] + # @param record [ApplicationRecord] + def notify_exception!(exception, record) + ExceptionNotifier.notify_exception(exception, data: { site: site.name, record_type: record.class.name, record_id: record.id }) + end end end From bf75d50cc35d578c410a117c42b32952a1fbab69 Mon Sep 17 00:00:00 2001 From: f Date: Tue, 12 Mar 2024 13:57:31 -0300 Subject: [PATCH 029/133] =?UTF-8?q?feat:=20modificar=20el=20estado=20de=20?= =?UTF-8?q?moderaci=C3=B3n=20en=20masa=20#15328=20#15327?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/activity_pubs_controller.rb | 45 +++++++------------ .../actor_moderations_controller.rb | 23 +++++----- .../instance_moderations_controller.rb | 15 ++++--- .../activity_pub/instance_moderation_job.rb | 13 ++---- app/models/activity_pub.rb | 22 --------- app/models/actor_moderation.rb | 13 ------ app/models/concerns/aasm_events_concern.rb | 20 +++++++++ app/models/fediblock_state.rb | 12 ----- app/models/instance_moderation.rb | 30 ++++++------- config/locales/es.yml | 13 ++++++ 10 files changed, 88 insertions(+), 118 deletions(-) diff --git a/app/controllers/activity_pubs_controller.rb b/app/controllers/activity_pubs_controller.rb index 1efe2b89..edece8f8 100644 --- a/app/controllers/activity_pubs_controller.rb +++ b/app/controllers/activity_pubs_controller.rb @@ -27,7 +27,7 @@ class ActivityPubsController < ApplicationController authorize activity_pubs action = params[:activity_pub_action].to_sym - method = :"#{action}!" + method = :"#{action}_all!" may = :"may_#{action}?" redirect_to_moderation_queue! @@ -35,36 +35,25 @@ class ActivityPubsController < ApplicationController return unless ActivityPub.events.include? action # Crear una sola remote flag por autore - if action == :report - message = remote_flag_params(activity_pubs.first).dig(:remote_flag_attributes, :message) - - activity_pubs.distinct.pluck(:actor_id).each do |actor_id| - remote_flag = ActivityPub::RemoteFlag.find_or_initialize_by(actor_id: actor_id, site_id: site.id) - remote_flag.message = message - # Lo estamos actualizando, con lo que lo vamos a volver a enviar - remote_flag.requeue if remote_flag.persisted? - remote_flag.save - # XXX: Idealmente todas las ActivityPub que enviamos pueden - # cambiar de estado, pero chequeamos de todas formas. - remote_flag.activity_pubs << (activity_pubs.where(actor_id: actor_id).to_a.select { |a| a.public_send(may) }) - end - end - ActivityPub.transaction do - activity_pubs.find_each do |activity_pub| - next unless activity_pub.public_send(may) + if action == :report + message = remote_flag_params(activity_pubs.first).dig(:remote_flag_attributes, :message) - activity_pub.public_send(method) - - flash[:success] = I18n.t('activity_pubs.action_on_several.success') - rescue Exception => e - ExceptionNotifier.notify_exception(e, - data: { site: site.name, params: params.permit!.to_h, - activity_pub: activity_pub.id }) - - flash.delete(:success) - flash[:error] = I18n.t('activity_pubs.action_on_several.error') + activity_pubs.distinct.pluck(:actor_id).each do |actor_id| + remote_flag = ActivityPub::RemoteFlag.find_or_initialize_by(actor_id: actor_id, site_id: site.id) + remote_flag.message = message + # Lo estamos actualizando, con lo que lo vamos a volver a enviar + remote_flag.requeue if remote_flag.persisted? + remote_flag.save + # XXX: Idealmente todas las ActivityPub que enviamos pueden + # cambiar de estado, pero chequeamos de todas formas. + remote_flag.activity_pubs << (activity_pubs.where(actor_id: actor_id).to_a.select { |a| a.public_send(may) }) + end end + + message = activity_pubs.public_send(method) ? :success : :error + + flash[message] = I18n.t("activity_pubs.action_on_several.#{message}") end end diff --git a/app/controllers/actor_moderations_controller.rb b/app/controllers/actor_moderations_controller.rb index 7c2c3d82..bc4a059b 100644 --- a/app/controllers/actor_moderations_controller.rb +++ b/app/controllers/actor_moderations_controller.rb @@ -37,7 +37,7 @@ class ActorModerationsController < ApplicationController authorize actor_moderations action = params[:actor_moderation_action].to_sym - method = :"#{action}!" + method = :"#{action}_all!" may = :"may_#{action}?" redirect_to_moderation_queue! @@ -45,20 +45,17 @@ class ActorModerationsController < ApplicationController return unless ActorModeration.events.include? action ActorModeration.transaction do - actor_moderations.find_each do |actor_moderation| - next unless actor_moderation.public_send(may) + if action == :report + actor_moderations.find_each do |actor_moderation| + next unless actor_moderation.public_send(may) - actor_moderation.update(actor_moderation_params(actor_moderation)) if action == :report - - actor_moderation.public_send(method) - - flash[:success] = I18n.t('actor_moderations.action_on_several.success') - rescue Exception => e - ExceptionNotifier.notify_exception(e, data: { site: site.name, params: params.permit!.to_h }) - - flash.delete(:success) - flash[:error] = I18n.t('actor_moderations.action_on_several.error') + actor_moderation.update(actor_moderation_params(actor_moderation)) + end end + + message = actor_moderation.public_send(method) ? :success : :error + + flash[message] = I18n.t("actor_moderations.action_on_several.#{message}") end end diff --git a/app/controllers/instance_moderations_controller.rb b/app/controllers/instance_moderations_controller.rb index 270f0588..06f5cfc1 100644 --- a/app/controllers/instance_moderations_controller.rb +++ b/app/controllers/instance_moderations_controller.rb @@ -10,6 +10,12 @@ class InstanceModerationsController < ApplicationController instance_moderation.public_send(:"#{event}!") if instance_moderation.public_send(:"may_#{event}?") + flash[:success] = I18n.t("instance_moderations.#{event}.success") + rescue Exception => e + ExceptionNotifier.notify_exception(e, data: { site: site.name, params: params.permit!.to_h }) + + flash[:error] = I18n.t("instance_moderations.#{event}.error") + ensure redirect_to_moderation_queue! end end @@ -20,17 +26,16 @@ class InstanceModerationsController < ApplicationController authorize instance_moderations action = params[:instance_moderation_action].to_sym - method = :"#{action}!" - may = :"may_#{action}?" + method = :"#{action}_all!" redirect_to_moderation_queue! return unless InstanceModeration.events.include? action InstanceModeration.transaction do - instance_moderations.find_each do |instance_moderation| - instance_moderation.public_send(method) if instance_moderation.public_send(may) - end + message = instance_moderations.public_send(method) ? :success : :error + + flash[:message] = I18n.t("instance_moderations.action_on_several.#{message}") end end diff --git a/app/jobs/activity_pub/instance_moderation_job.rb b/app/jobs/activity_pub/instance_moderation_job.rb index 17def46e..e28be84e 100644 --- a/app/jobs/activity_pub/instance_moderation_job.rb +++ b/app/jobs/activity_pub/instance_moderation_job.rb @@ -14,7 +14,6 @@ class ActivityPub end instances = ActivityPub::Instance.where(hostname: hostnames) - success = true Site.transaction do # Crea todas las moderaciones de instancia con un estado por @@ -22,17 +21,11 @@ class ActivityPub instances.find_each do |instance| # Esto bloquea cada una individualmente en la Social Inbox, # idealmente son pocas instancias las que aparecen. - site.instance_moderations.find_or_create_by(instance: instance).tap do |instance_moderation| - instance_moderation.block! if instance_moderation.may_block? - # Notificar todos los errores sin detener la ejecución - rescue Exception => e - ExceptionNotifier.notify_exception(e, data: { site: site.name, instance_moderation: instance_moderation.id }) - success = false - end + site.instance_moderations.find_or_create_by(instance: instance) end - end - success + site.instance_moderations.where(instance_id: instances.ids).block_all! + end end end end diff --git a/app/models/activity_pub.rb b/app/models/activity_pub.rb index fce340db..38bf3b3a 100644 --- a/app/models/activity_pub.rb +++ b/app/models/activity_pub.rb @@ -38,28 +38,6 @@ class ActivityPub < ApplicationRecord end end - # Permite todos los comentarios. No podemos hacer acciones en masa - # sobre comentarios en la Social Inbox, con lo que tenemos que - # comunicarnos individualmente. - def self.allow_all! - self.find_each do |activity_pub| - activity_pub.allow! if activity_pub.may_allow? - rescue Exception => e - notify_exception!(e, activity_pub) - end - end - - # Rechaza todos los comentarios. No podemos hacer acciones en masa - # sobre comentarios en la Social Inbox, con lo que tenemos que - # comunicarnos individualmente. - def self.reject_all! - self.find_each do |activity_pub| - activity_pub.reject! if activity_pub.may_reject? - rescue Exception => e - notify_exception!(e, activity_pub) - end - end - aasm do # Todavía no hay una decisión sobre el objeto state :paused, initial: true diff --git a/app/models/actor_moderation.rb b/app/models/actor_moderation.rb index a510f1eb..7cb8827d 100644 --- a/app/models/actor_moderation.rb +++ b/app/models/actor_moderation.rb @@ -14,19 +14,6 @@ class ActorModeration < ApplicationRecord accepts_nested_attributes_for :remote_flag - # Bloquea todes les Actores bloqueables - def self.block_all! - self.update_all(aasm_state: 'blocked', updated_at: Time.now) - end - - def self.pause_all! - self.update_all(aasm_state: 'paused', updated_at: Time.now) - end - - def self.remove_all! - self.update_all(aasm_state: 'removed', updated_at: Time.now) - end - aasm do state :paused, initial: true state :allowed diff --git a/app/models/concerns/aasm_events_concern.rb b/app/models/concerns/aasm_events_concern.rb index b189dbe4..967a61b3 100644 --- a/app/models/concerns/aasm_events_concern.rb +++ b/app/models/concerns/aasm_events_concern.rb @@ -28,6 +28,26 @@ module AasmEventsConcern aasm.states.map(&:name) - self::IGNORED_STATES end + # Define un método que cambia el estado para todos los objetos del + # scope actual. + # + # @return [Bool] Si hubo al menos un error, devuelve false. + self.events.each do |event| + define_singleton_method(:"#{event}_all!") do + success = true + + self.find_each do |object| + object.public_send(:"#{event}!") if object.public_send(:"may_#{event}?") + rescue Exception => e + success = false + + notify_exception! e, object + end + + success + end + end + # Envía notificación de errores # # @param exception [Exception] diff --git a/app/models/fediblock_state.rb b/app/models/fediblock_state.rb index e50abaef..dfdc4e34 100644 --- a/app/models/fediblock_state.rb +++ b/app/models/fediblock_state.rb @@ -39,12 +39,6 @@ class FediblockState < ApplicationRecord # Luego esta tarea crea las que falten e ignora las que ya se # bloquearon. ActivityPub::InstanceModerationJob.perform_now(site: site, hostnames: fediblock.hostnames) - - # Bloquear a todes les Actores de las instancias bloqueadas para - # indicarle a le usuarie que les tiene que desbloquear - # manualmente. - actor_ids = ActivityPub::Actor.where(instance_id: instance_ids).ids - ActorModeration.where(actor_id: actor_ids).paused.block_all! end end @@ -66,12 +60,6 @@ class FediblockState < ApplicationRecord # bloqueadas por otros fediblocks. instance_ids = ActivityPub::Instance.where(hostname: unique_hostnames).ids site.instance_moderations.where(instance_id: instance_ids).pause_all! - - # Volver a pausar todes les actores de esta instancia que fueron - # bloqueades, a menos que hayan sido bloqueades por otro - # fediblock. - actor_ids = ActivityPub::Actor.where(instance_id: instance_ids).ids - ActorModeration.where(actor_id: actor_ids).blocked.pause_all! end end end diff --git a/app/models/instance_moderation.rb b/app/models/instance_moderation.rb index ef04b7ff..918c6ad0 100644 --- a/app/models/instance_moderation.rb +++ b/app/models/instance_moderation.rb @@ -11,26 +11,13 @@ class InstanceModeration < ApplicationRecord belongs_to :site belongs_to :instance, class_name: 'ActivityPub::Instance' - # Traer todas las instancias bloqueables, según la máquina de estados, - # todas las que no estén bloqueadas ya. - scope :may_block, -> { where.not(aasm_state: 'blocked') } - scope :may_pause, -> { where.not(aasm_state: 'paused') } - - # Bloquear instancias en masa - def self.block_all! - self.may_block.update_all(aasm_state: 'blocked', updated_at: Time.now) - end - - # Pausar instancias en masa - def self.pause_all! - self.may_pause.update_all(aasm_state: 'paused', updated_at: Time.now) - end - aasm do state :paused, initial: true state :allowed state :blocked + # Al volver la instancia a pausa no cambiamos el estado de + # moderación de actores pre-existente. event :pause do transitions from: %i[allowed blocked], to: :paused @@ -39,23 +26,36 @@ class InstanceModeration < ApplicationRecord end end + # Al permitir, también permitimos todes les actores que no hayan + # tenido acciones de moderación. event :allow do transitions from: %i[paused blocked], to: :allowed before do allow_remotely! + + site.actor_moderations.paused.where(actor_id: actor_ids).allow_all! end end + # Al bloquear, también bloqueamos a todes les actores que no hayan + # tenido acciones de moderación. event :block do transitions from: %i[paused allowed], to: :blocked before do block_remotely! + + site.actor_moderations.paused.where(actor_id: actor_ids).block_all! end end end + # @return [Array] + def actor_ids + ActivityPub::Actor.where(instance_id: self.instance_id).ids + end + # Elimina la instancia de todas las listas # # @return [Boolean] diff --git a/config/locales/es.yml b/config/locales/es.yml index 3df608f5..3cc6c49a 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -145,6 +145,19 @@ es: report: success: "Cuenta reportada a su instancia." error: "No se pudo reportar la cuenta. Hemos recibido el reporte y lo estaremos verificando." + instance_moderations: + action_on_several: + success: "Se ha modificado el estado de moderación de varias instancias. Podés encontrarlas usando los filtros en la sección Instancias." + error: "Hubo un error al modificar el estado de moderación de varias instancias. Hemos recibido el reporte y lo estaremos verificando." + pause: + success: "Instancia pausada. Todos los comentarios y cuentas de esta instancia necesitan ser aprobados manualmente. No se ha modificado el estado de moderación de cuentas y comentarios pre-existentes." + error: "No se pudo pausar la instancia. Hemos recibido el reporte y lo estaremos verificando." + allow: + success: "Instancia permitida. Todos los comentarios y cuentas pendientes de moderación fueron aprobados y los próximos serán aprobados inmediatamente." + error: "No se pudo permitir la instancia. Hemos recibido el reporte y lo estaremos verificando." + block: + success: "Instancia bloqueada. Todos los comentarios y cuentas serán rechazados inmediatamente. Todos los comentarios y cuentas pendientes de moderación fueron rechazados." + error: "No se pudo bloquear la instancia. Hemos recibido el reporte y lo estaremos verificando." fediblock_states: action_on_several: success: "Se habilitaron las listas de bloqueo, podés encontrar las instancias filtrando por Bloqueadas. Todas las cuentas de estas instancias pendientes de moderación han sido bloqueadas. Podés activarlas individualmente en la sección Cuentas." From d2d327dc848b6b5e4abd318d71575effe125c89e Mon Sep 17 00:00:00 2001 From: f Date: Tue, 12 Mar 2024 14:24:38 -0300 Subject: [PATCH 030/133] =?UTF-8?q?fixup!=20feat:=20modificar=20el=20estad?= =?UTF-8?q?o=20de=20moderaci=C3=B3n=20en=20masa=20#15328=20#15327?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/activity_pub.rb | 6 +++--- app/models/actor_moderation.rb | 6 +++--- app/models/instance_moderation.rb | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/models/activity_pub.rb b/app/models/activity_pub.rb index 38bf3b3a..61120a58 100644 --- a/app/models/activity_pub.rb +++ b/app/models/activity_pub.rb @@ -8,12 +8,12 @@ # # @see {https://www.w3.org/TR/activitypub/#client-to-server-interactions} class ActivityPub < ApplicationRecord - include AASM - include AasmEventsConcern - IGNORED_EVENTS = %i[remove] IGNORED_STATES = %i[removed] + include AASM + include AasmEventsConcern + belongs_to :instance belongs_to :site belongs_to :object, polymorphic: true diff --git a/app/models/actor_moderation.rb b/app/models/actor_moderation.rb index 7cb8827d..04c96ac0 100644 --- a/app/models/actor_moderation.rb +++ b/app/models/actor_moderation.rb @@ -2,12 +2,12 @@ # Mantiene la relación entre Site y Actor class ActorModeration < ApplicationRecord - include AASM - include AasmEventsConcern - IGNORED_EVENTS = %i[remove] IGNORED_STATES = %i[removed] + include AASM + include AasmEventsConcern + belongs_to :site belongs_to :remote_flag, optional: true, class_name: 'ActivityPub::RemoteFlag' belongs_to :actor, class_name: 'ActivityPub::Actor' diff --git a/app/models/instance_moderation.rb b/app/models/instance_moderation.rb index 918c6ad0..1ed7d2c0 100644 --- a/app/models/instance_moderation.rb +++ b/app/models/instance_moderation.rb @@ -2,12 +2,12 @@ # Mantiene el registro de relaciones entre sitios e instancias class InstanceModeration < ApplicationRecord - include AASM - include AasmEventsConcern - IGNORED_EVENTS = [] IGNORED_STATES = [] + include AASM + include AasmEventsConcern + belongs_to :site belongs_to :instance, class_name: 'ActivityPub::Instance' From e783390747f2ab054f4765b353ca575ea3d7b802 Mon Sep 17 00:00:00 2001 From: f Date: Tue, 12 Mar 2024 14:28:26 -0300 Subject: [PATCH 031/133] =?UTF-8?q?fixup!=20fixup!=20feat:=20modificar=20e?= =?UTF-8?q?l=20estado=20de=20moderaci=C3=B3n=20en=20masa=20#15328=20#15327?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/locales/en.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/config/locales/en.yml b/config/locales/en.yml index 48d9ca61..bc6b1285 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -146,6 +146,19 @@ en: report: success: "Account reported." error: "There was an error while reporting the account. We received a report and will be acting on it soon." + instance_moderations: + action_on_several: + success: "Several instances have changed moderation state. You can find them using the filters on the Instances section." + error: "There was an error while changing moderation state. We received a report and will be acting on it soon." + pause: + success: "Instance paused. All of their comments and accounts will need to be moderated individually." + error: "There was an error while pausing the instance. We received a report and will be acting on it soon." + allow: + success: "Instance allowed. All of their comments and accounts will be approved automatically. Any pending comments and accounts have been also approved." + error: "There was an error while allowing the instance. We received a report and will be acting on it soon." + block: + success: "Instance blocked. All of their comments and accounts will be rejected automatically. Any pending comments and accounts have been also rejected." + error: "There was an error while blocking the instance. We received a report and will be acting on it soon." fediblock_states: action_on_several: success: "Blocklists have been enabled, you can find their instances by filtering by Blocked. Any pending account from these instances has also been blocked. You can approve them individually on the Accounts section." From 20c6d6af5042360736d8ebd212da2b99e118ec41 Mon Sep 17 00:00:00 2001 From: f Date: Tue, 12 Mar 2024 14:30:43 -0300 Subject: [PATCH 032/133] feat: eliminar todos los comentarios de une actore eliminade --- app/models/actor_moderation.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/models/actor_moderation.rb b/app/models/actor_moderation.rb index 04c96ac0..783160c7 100644 --- a/app/models/actor_moderation.rb +++ b/app/models/actor_moderation.rb @@ -67,6 +67,10 @@ class ActorModeration < ApplicationRecord # mostrarlo y todas sus actividades. event :remove do transitions to: :removed + + before do + site.activity_pubs.where(actor_id: self.actor_id).remove_all! + end end end From 12f77e46581cc539fc6655f8f282c2d72147e71e Mon Sep 17 00:00:00 2001 From: f Date: Tue, 12 Mar 2024 15:57:04 -0300 Subject: [PATCH 033/133] fix: actualizar cliente --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 366b58a5..7b19ba75 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -166,7 +166,7 @@ GEM devise_invitable (2.0.9) actionmailer (>= 5.0) devise (>= 4.6) - distributed-press-api-client (0.4.0rc3) + distributed-press-api-client (0.4.0) addressable (~> 2.3, >= 2.3.0) climate_control dry-schema @@ -626,7 +626,7 @@ DEPENDENCIES devise devise-i18n devise_invitable - distributed-press-api-client (~> 0.4.0rc3) + distributed-press-api-client (~> 0.4.0) dotenv-rails down ed25519 From 3c07ddf12c946303fddb5403b166faa6905b4ea1 Mon Sep 17 00:00:00 2001 From: f Date: Tue, 12 Mar 2024 15:59:42 -0300 Subject: [PATCH 034/133] =?UTF-8?q?fixup!=20fixup!=20fixup!=20feat:=20modi?= =?UTF-8?q?ficar=20el=20estado=20de=20moderaci=C3=B3n=20en=20masa=20#15328?= =?UTF-8?q?=20#15327?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/activity_pub/remote_flag.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/models/activity_pub/remote_flag.rb b/app/models/activity_pub/remote_flag.rb index 76143414..1b6f5c5f 100644 --- a/app/models/activity_pub/remote_flag.rb +++ b/app/models/activity_pub/remote_flag.rb @@ -2,6 +2,9 @@ class ActivityPub class RemoteFlag < ApplicationRecord + IGNORED_EVENTS = [] + IGNORED_STATES = [] + include AASM include AasmEventsConcern From 4fde9793d733426a50ccf86104841cbdfe3140f8 Mon Sep 17 00:00:00 2001 From: f Date: Tue, 12 Mar 2024 17:09:21 -0300 Subject: [PATCH 035/133] fix: permitir que la tarea se reporte como hecha --- app/models/deploy_social_distributed_press.rb | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/models/deploy_social_distributed_press.rb b/app/models/deploy_social_distributed_press.rb index b7525dca..c7a103a4 100644 --- a/app/models/deploy_social_distributed_press.rb +++ b/app/models/deploy_social_distributed_press.rb @@ -13,12 +13,11 @@ class DeploySocialDistributedPress < Deploy key = Shellwords.escape file.path dest = Shellwords.escape destination - run %(bundle exec jekyll notify --trace --key #{key} --destination "#{dest}"), output: output + run(%(bundle exec jekyll notify --trace --key #{key} --destination "#{dest}"), output: output).tap do |_| + create_hooks! + enable_fediblocks! + end end - - - create_hooks! - enable_fediblocks! end # Igual que DeployLocal From d44bcc4098d0959d78c89ac564ee78ec2d01248b Mon Sep 17 00:00:00 2001 From: f Date: Tue, 12 Mar 2024 17:30:47 -0300 Subject: [PATCH 036/133] fix: typo --- app/jobs/activity_pub/remote_flag_job.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/jobs/activity_pub/remote_flag_job.rb b/app/jobs/activity_pub/remote_flag_job.rb index 26db937a..f5650d53 100644 --- a/app/jobs/activity_pub/remote_flag_job.rb +++ b/app/jobs/activity_pub/remote_flag_job.rb @@ -13,7 +13,7 @@ class ActivityPub self.priority = 30 def perform(remote_flag:) - return if remote_flag.can_queue? + return if remote_flag.may_queue? remote_flag.queue! From 45d56502c11bb9c65f5538e08173052699f845ce Mon Sep 17 00:00:00 2001 From: f Date: Tue, 12 Mar 2024 17:44:40 -0300 Subject: [PATCH 037/133] fix: aprobar --- app/models/actor_moderation.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/actor_moderation.rb b/app/models/actor_moderation.rb index 783160c7..ad29739f 100644 --- a/app/models/actor_moderation.rb +++ b/app/models/actor_moderation.rb @@ -37,7 +37,7 @@ class ActorModeration < ApplicationRecord before do allow_remotely! - site.activity_pubs.paused.where(actor_id: self.actor_id).allow_all! + site.activity_pubs.paused.where(actor_id: self.actor_id).approve_all! end end From 32042f32e23d95bb8dcde1976eb4d7203d411753 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 13 Mar 2024 11:05:28 -0300 Subject: [PATCH 038/133] =?UTF-8?q?fix:=20no=20ignorar=20ning=C3=BAn=20eve?= =?UTF-8?q?nto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes #15366 closes #15367 closes #15368 closes #15369 closes #15370 closes #15371 closes #15372 closes #15373 closes #15374 closes #15375 closes #15376 closes #15377 closes #15378 closes #15379 closes #15380 closes #15381 closes #15382 closes #15383 closes #15384 closes #15385 closes #15386 closes #15387 closes #15388 closes #15389 closes #15390 closes #15391 closes #15392 closes #15393 closes #15394 closes #15395 closes #15396 closes #15397 closes #15398 closes #15399 closes #15400 closes #15401 closes #15402 closes #15403 closes #15404 closes #15405 closes #15406 closes #15407 closes #15408 closes #15409 closes #15410 closes #15411 closes #15412 closes #15413 closes #15414 closes #15415 closes #15416 closes #15417 closes #15418 closes #15419 closes #15420 closes #15421 closes #15422 closes #15423 closes #15424 closes #15425 closes #15426 closes #15427 closes #15428 closes #15429 closes #15430 closes #15431 closes #15432 closes #15433 closes #15434 closes #15435 closes #15436 closes #15437 closes #15438 closes #15439 closes #15440 closes #15441 closes #15442 closes #15443 closes #15444 closes #15445 closes #15446 closes #15447 closes #15448 closes #15449 closes #15450 closes #15451 closes #15452 closes #15453 closes #15454 closes #15455 closes #15456 closes #15457 closes #15458 closes #15459 closes #15460 closes #15461 closes #15462 closes #15463 closes #15464 closes #15465 closes #15466 closes #15467 closes #15468 closes #15469 closes #15470 closes #15471 closes #15472 closes #15473 closes #15477 closes #15478 closes #15479 closes #15480 closes #15481 closes #15482 closes #15483 closes #15484 closes #15485 closes #15486 closes #15487 closes #15488 closes #15489 closes #15490 closes #15491 closes #15492 closes #15493 closes #15494 closes #15495 closes #15496 closes #15497 closes #15498 closes #15499 closes #15500 closes #15501 closes #15502 closes #15503 closes #15504 closes #15505 closes #15506 closes #15507 closes #15508 closes #15509 closes #15510 closes #15511 closes #15512 closes #15513 closes #15514 closes #15515 closes #15516 closes #15517 closes #15518 closes #15519 closes #15520 closes #15521 closes #15522 closes #15523 closes #15524 closes #15525 closes #15526 closes #15527 closes #15528 closes #15529 closes #15530 closes #15531 closes #15532 closes #15533 closes #15534 closes #15535 closes #15536 closes #15537 closes #15538 closes #15539 closes #15540 closes #15541 closes #15542 closes #15543 closes #15544 closes #15545 closes #15546 closes #15547 closes #15548 closes #15549 --- app/models/concerns/aasm_events_concern.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/concerns/aasm_events_concern.rb b/app/models/concerns/aasm_events_concern.rb index 967a61b3..08b4edd7 100644 --- a/app/models/concerns/aasm_events_concern.rb +++ b/app/models/concerns/aasm_events_concern.rb @@ -32,7 +32,7 @@ module AasmEventsConcern # scope actual. # # @return [Bool] Si hubo al menos un error, devuelve false. - self.events.each do |event| + self.aasm.events.map(&:name).each do |event| define_singleton_method(:"#{event}_all!") do success = true From 40edccf8bbbe9c272245a11674d9a0209ec57955 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 13 Mar 2024 11:15:31 -0300 Subject: [PATCH 039/133] =?UTF-8?q?fixup!=20fix:=20no=20ignorar=20ning?= =?UTF-8?q?=C3=BAn=20evento?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/activity_pub.rb | 4 +++- app/models/activity_pub/remote_flag.rb | 4 +++- app/models/actor_moderation.rb | 4 +++- app/models/instance_moderation.rb | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/models/activity_pub.rb b/app/models/activity_pub.rb index 61120a58..a6b1401b 100644 --- a/app/models/activity_pub.rb +++ b/app/models/activity_pub.rb @@ -12,7 +12,6 @@ class ActivityPub < ApplicationRecord IGNORED_STATES = %i[removed] include AASM - include AasmEventsConcern belongs_to :instance belongs_to :site @@ -91,6 +90,9 @@ class ActivityPub < ApplicationRecord end end + # Definir eventos en masa + include AasmEventsConcern + def reject_remotely! raise unless site.social_inbox.inbox.reject(id: object.uri).ok? end diff --git a/app/models/activity_pub/remote_flag.rb b/app/models/activity_pub/remote_flag.rb index 1b6f5c5f..70f09dcc 100644 --- a/app/models/activity_pub/remote_flag.rb +++ b/app/models/activity_pub/remote_flag.rb @@ -6,7 +6,6 @@ class ActivityPub IGNORED_STATES = [] include AASM - include AasmEventsConcern aasm do state :waiting, initial: true @@ -26,6 +25,9 @@ class ActivityPub end end + # Definir eventos en masa + include AasmEventsConcern + belongs_to :actor belongs_to :site diff --git a/app/models/actor_moderation.rb b/app/models/actor_moderation.rb index ad29739f..7e68f60b 100644 --- a/app/models/actor_moderation.rb +++ b/app/models/actor_moderation.rb @@ -6,7 +6,6 @@ class ActorModeration < ApplicationRecord IGNORED_STATES = %i[removed] include AASM - include AasmEventsConcern belongs_to :site belongs_to :remote_flag, optional: true, class_name: 'ActivityPub::RemoteFlag' @@ -74,6 +73,9 @@ class ActorModeration < ApplicationRecord end end + # Definir eventos en masa + include AasmEventsConcern + def pause_remotely! raise unless actor.mention && diff --git a/app/models/instance_moderation.rb b/app/models/instance_moderation.rb index 1ed7d2c0..5b246cee 100644 --- a/app/models/instance_moderation.rb +++ b/app/models/instance_moderation.rb @@ -6,7 +6,6 @@ class InstanceModeration < ApplicationRecord IGNORED_STATES = [] include AASM - include AasmEventsConcern belongs_to :site belongs_to :instance, class_name: 'ActivityPub::Instance' @@ -51,6 +50,9 @@ class InstanceModeration < ApplicationRecord end end + # Definir eventos en masa + include AasmEventsConcern + # @return [Array] def actor_ids ActivityPub::Actor.where(instance_id: self.instance_id).ids From ef482aaedf2d8696408a4d40e642df8730b70447 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 13 Mar 2024 11:41:04 -0300 Subject: [PATCH 040/133] =?UTF-8?q?fix:=20el=20m=C3=A9todo=20es=20de=20cla?= =?UTF-8?q?se?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes #15553 --- app/models/concerns/aasm_events_concern.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/concerns/aasm_events_concern.rb b/app/models/concerns/aasm_events_concern.rb index 08b4edd7..70fb5ed2 100644 --- a/app/models/concerns/aasm_events_concern.rb +++ b/app/models/concerns/aasm_events_concern.rb @@ -52,8 +52,8 @@ module AasmEventsConcern # # @param exception [Exception] # @param record [ApplicationRecord] - def notify_exception!(exception, record) - ExceptionNotifier.notify_exception(exception, data: { site: site.name, record_type: record.class.name, record_id: record.id }) + def self.notify_exception!(exception, record) + ExceptionNotifier.notify_exception(exception, data: { record_type: record.class.name, record_id: record.id }) end end end From f794e8057b3248dfafd1b259a0100a95db847c87 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 13 Mar 2024 15:29:34 -0300 Subject: [PATCH 041/133] feat: no hacer acciones en cascada #15327 #15328 --- .../activity_pub/instance_moderation_job.rb | 13 ++++++++---- app/models/actor_moderation.rb | 8 ++------ app/models/concerns/aasm_events_concern.rb | 13 ++++++++++++ app/models/fediblock_state.rb | 16 +++++---------- app/models/instance_moderation.rb | 12 ++++------- config/locales/en.yml | 20 +++++++++---------- config/locales/es.yml | 20 +++++++++---------- 7 files changed, 53 insertions(+), 49 deletions(-) diff --git a/app/jobs/activity_pub/instance_moderation_job.rb b/app/jobs/activity_pub/instance_moderation_job.rb index e28be84e..214f8dd4 100644 --- a/app/jobs/activity_pub/instance_moderation_job.rb +++ b/app/jobs/activity_pub/instance_moderation_job.rb @@ -3,11 +3,10 @@ class ActivityPub # Bloquea varias instancias de una sola vez class InstanceModerationJob < ApplicationJob - self.priority = 50 - # @param :site [Site] # @param :hostnames [Array] - def perform(site:, hostnames:) + # @param :perform_remotely [Bool] + def perform(site:, hostnames:, perform_remotely: true) # Crear las instancias que no existan todavía hostnames.each do |hostname| ActivityPub::Instance.find_or_create_by(hostname: hostname) @@ -24,7 +23,13 @@ class ActivityPub site.instance_moderations.find_or_create_by(instance: instance) end - site.instance_moderations.where(instance_id: instances.ids).block_all! + scope = site.instance_moderations.where(instance_id: instances.ids) + + if perform_remotely + scope.block_all! + else + scope.block_all_without_callbacks! + end end end end diff --git a/app/models/actor_moderation.rb b/app/models/actor_moderation.rb index 7e68f60b..3b183a4f 100644 --- a/app/models/actor_moderation.rb +++ b/app/models/actor_moderation.rb @@ -28,27 +28,23 @@ class ActorModeration < ApplicationRecord end end - # Al permitir una cuenta se permiten todos los comentarios + # Al permitir una cuenta no se permiten todos los comentarios # pendientes de moderación que ya hizo. event :allow do transitions from: %i[paused blocked reported], to: :allowed before do allow_remotely! - - site.activity_pubs.paused.where(actor_id: self.actor_id).approve_all! end end - # Al bloquear una cuenta se bloquean todos los comentarios + # Al bloquear una cuenta no se bloquean todos los comentarios # pendientes de moderación que hizo. event :block do transitions from: %i[paused allowed], to: :blocked before do block_remotely! - - site.activity_pubs.paused.where(actor_id: self.actor_id).reject_all! end end diff --git a/app/models/concerns/aasm_events_concern.rb b/app/models/concerns/aasm_events_concern.rb index 70fb5ed2..4de5f748 100644 --- a/app/models/concerns/aasm_events_concern.rb +++ b/app/models/concerns/aasm_events_concern.rb @@ -46,6 +46,19 @@ module AasmEventsConcern success end + + # Ejecuta la transición del evento en la base de datos sin + # ejecutar los callbacks, sin modificar los items del scope que no + # pueden transicionar. + # + # @return [Integer] Registros modificados + define_singleton_method(:"#{event}_all_without_callbacks!") do + aasm_event = self.aasm.events.find { |e| e.name == event } + to_state = aasm_event.transitions.map(&:to).first + from_states = aasm_event.transitions.map(&:from) + + self.unscope(where: :aasm_state).where(aasm_state: from_states).update_all(aasm_state: to_state, updated_at: Time.now) + end end # Envía notificación de errores diff --git a/app/models/fediblock_state.rb b/app/models/fediblock_state.rb index dfdc4e34..9dde1db3 100644 --- a/app/models/fediblock_state.rb +++ b/app/models/fediblock_state.rb @@ -30,15 +30,9 @@ class FediblockState < ApplicationRecord # Bloquear todos las instancias de este Fediblock enable_remotely! list_names(fediblock.hostnames) - # Al actualizar el estado en masa garantizamos que las - # instancias que ya existen queden sincronizadas con el bloqueo - # en masa que acabamos de hacer. - instance_ids = fediblock.instances.ids - site.instance_moderations.where(instance_id: instance_ids).block_all! - # Luego esta tarea crea las que falten e ignora las que ya se # bloquearon. - ActivityPub::InstanceModerationJob.perform_now(site: site, hostnames: fediblock.hostnames) + ActivityPub::InstanceModerationJob.perform_now(site: site, hostnames: fediblock.hostnames, perform_remotely: false) end end @@ -56,10 +50,10 @@ class FediblockState < ApplicationRecord # otros fediblocks disable_remotely! list_names(unique_hostnames) - # Pausar todas las moderaciones de las instancias que no estén - # bloqueadas por otros fediblocks. - instance_ids = ActivityPub::Instance.where(hostname: unique_hostnames).ids - site.instance_moderations.where(instance_id: instance_ids).pause_all! + # Pausar todas las moderaciones de las instancias que no estén + # bloqueadas por otros fediblocks. + instance_ids = ActivityPub::Instance.where(hostname: unique_hostnames).ids + site.instance_moderations.where(instance_id: instance_ids).pause_all_without_callbacks! end end end diff --git a/app/models/instance_moderation.rb b/app/models/instance_moderation.rb index 5b246cee..9cb6ffdc 100644 --- a/app/models/instance_moderation.rb +++ b/app/models/instance_moderation.rb @@ -25,27 +25,23 @@ class InstanceModeration < ApplicationRecord end end - # Al permitir, también permitimos todes les actores que no hayan - # tenido acciones de moderación. + # Al permitir, solo bloqueamos la instancia, sin modificar el estado + # de les actores y comentarios retroactivamente. event :allow do transitions from: %i[paused blocked], to: :allowed before do allow_remotely! - - site.actor_moderations.paused.where(actor_id: actor_ids).allow_all! end end - # Al bloquear, también bloqueamos a todes les actores que no hayan - # tenido acciones de moderación. + # Al bloquear, solo bloqueamos la instancia, sin modificar el estado + # de les actores y comentarios retroactivamente. event :block do transitions from: %i[paused allowed], to: :blocked before do block_remotely! - - site.actor_moderations.paused.where(actor_id: actor_ids).block_all! end end end diff --git a/config/locales/en.yml b/config/locales/en.yml index bc6b1285..d745e2d5 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -132,38 +132,38 @@ en: error: "There was an error while reporting the comment. We received a report and will be acting on it soon." actor_moderations: action_on_several: - success: "Several accounts have changed moderation state. You can find them using the filters on the Accounts section." + success: "Several accounts have changed moderation state. You can find them using the filters on the Accounts section. No action was performed over existing Comments." error: "There was an error while changing moderation state. We received a report and will be acting on it soon." pause: - success: "Account paused. All of their comments will need to be moderated individually on the Comments section." + success: "Account paused. No action was performed on existing Comments." error: "There was an error while pausing the account. We received a report and will be acting on it soon." allow: - success: "Account allowed. All of their comments will be approved automatically. Any pending comments have been approved." + success: "Account allowed. All of their comments from now on will be approved automatically. No action was performed over existing Comments." error: "There was an error while allowing the account. We received a report and will be acting on it soon." block: - success: "Account blocked. All of their comments will be rejected automatically. Any pending comments have been rejected. If you want to report it to their instance, please use the Report button." + success: "Account blocked. All of their comments from now on will be rejected automatically. No action was performed over existing Comments. If you want to report it to their instance, please use the Report button." error: "There was an error while blocking the account. We received a report and will be acting on it soon." report: success: "Account reported." error: "There was an error while reporting the account. We received a report and will be acting on it soon." instance_moderations: action_on_several: - success: "Several instances have changed moderation state. You can find them using the filters on the Instances section." + success: "Several instances have changed moderation state. You can find them using the filters on the Instances section. No action was performed over existing Accounts and Comments." error: "There was an error while changing moderation state. We received a report and will be acting on it soon." pause: - success: "Instance paused. All of their comments and accounts will need to be moderated individually." + success: "Instance paused. All of their comments and accounts from now on will need to be moderated individually. No action was performed over existing Accounts and Comments." error: "There was an error while pausing the instance. We received a report and will be acting on it soon." allow: - success: "Instance allowed. All of their comments and accounts will be approved automatically. Any pending comments and accounts have been also approved." + success: "Instance allowed. All of their comments and accounts from now on will be approved automatically. No action was performed over existing Accounts and Comments." error: "There was an error while allowing the instance. We received a report and will be acting on it soon." block: - success: "Instance blocked. All of their comments and accounts will be rejected automatically. Any pending comments and accounts have been also rejected." + success: "Instance blocked. All of their comments and accounts from now on will be rejected automatically. No action was performed over existing Accounts and Comments." error: "There was an error while blocking the instance. We received a report and will be acting on it soon." fediblock_states: action_on_several: - success: "Blocklists have been enabled, you can find their instances by filtering by Blocked. Any pending account from these instances has also been blocked. You can approve them individually on the Accounts section." + success: "Blocklists have been enabled, you can find their instances by filtering by Blocked. You can approve them individually on the Accounts section. No action was performed over existing Accounts and Comments." error: "There was an error while enabling or disabling blocklists. We received a report and will be acting on it soon." - custom_blocklist_success: "Custom blocklist has been added, you can find the instances by filtering by Blocked. Any pending account from these instances has also been blocked. You can approve them individually on the Accounts section." + custom_blocklist_success: "Custom blocklist has been added, you can find the instances by filtering by Blocked. No action was performed over existing Accounts and Comments." custom_blocklist_error: "There was an error while adding a custom blocklist. We received a report and will be acting on it soon." moderation_queue: everything: 'Select all' diff --git a/config/locales/es.yml b/config/locales/es.yml index 3cc6c49a..a7b8d452 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -131,38 +131,38 @@ es: error: "No se puedo reportar el comentario. Hemos recibido el reporte y lo estaremos verificando." actor_moderations: action_on_several: - success: "Se ha modificado el estado de moderación de varias cuentas. Podés encontrarlas usando los filtros en la sección Cuentas." + success: "Se ha modificado el estado de moderación de varias cuentas. Podés encontrarlas usando los filtros en la sección Cuentas. No se modificaron comentarios pre-existentes." error: "Hubo un error al modificar el estado de moderación de varias cuentas. Hemos recibido el reporte y lo estaremos verificando." pause: - success: "Cuenta pausada. Todos los comentarios que haga necesitan ser aprobados manualmente en la sección Comentarios." + success: "Cuenta pausada. Todos los comentarios que haga necesitan ser aprobados manualmente en la sección Comentarios. No se modificaron comentarios pre-existentes." error: "No se pudo pausar la cuenta. Hemos recibido el reporte y lo estaremos verificando." allow: - success: "Cuenta permitida. Todos los comentarios que haga serán aprobados inmediatamente. Todos los comentarios pendientes de moderación fueron aprobados." + success: "Cuenta permitida. Todos los comentarios que haga serán aprobados inmediatamente. No se modificaron comentarios pre-existentes." error: "No se pudo permitir la cuenta. Hemos recibido el reporte y lo estaremos verificando." block: - success: "Cuenta bloqueada. Todos los comentarios que haga serán rechazados inmediatamente. Todos los comentarios pendientes de moderación fueron rechazados. Si querés reportarla a su instancia, podés usar el botón Reportar." + success: "Cuenta bloqueada. Todos los comentarios que haga serán rechazados inmediatamente. Si querés reportarla a su instancia, podés usar el botón Reportar. No se modificaron comentarios pre-existentes." error: "No se pudo bloquear la cuenta. Hemos recibido el reporte y lo estaremos verificando." report: success: "Cuenta reportada a su instancia." error: "No se pudo reportar la cuenta. Hemos recibido el reporte y lo estaremos verificando." instance_moderations: action_on_several: - success: "Se ha modificado el estado de moderación de varias instancias. Podés encontrarlas usando los filtros en la sección Instancias." + success: "Se ha modificado el estado de moderación de varias instancias. Podés encontrarlas usando los filtros en la sección Instancias. No se modificaron cuentas y comentarios pre-existentes." error: "Hubo un error al modificar el estado de moderación de varias instancias. Hemos recibido el reporte y lo estaremos verificando." pause: - success: "Instancia pausada. Todos los comentarios y cuentas de esta instancia necesitan ser aprobados manualmente. No se ha modificado el estado de moderación de cuentas y comentarios pre-existentes." + success: "Instancia pausada. A partir de ahora, todos los comentarios y cuentas de esta instancia necesitan ser aprobados manualmente. No se ha modificado el estado de moderación de cuentas ni comentarios pre-existentes." error: "No se pudo pausar la instancia. Hemos recibido el reporte y lo estaremos verificando." allow: - success: "Instancia permitida. Todos los comentarios y cuentas pendientes de moderación fueron aprobados y los próximos serán aprobados inmediatamente." + success: "Instancia permitida. A partir de ahora, todos los comentarios y cuentas pendientes serán aprobados inmediatamente. No se modificaron cuentas ni comentarios pre-existentes." error: "No se pudo permitir la instancia. Hemos recibido el reporte y lo estaremos verificando." block: - success: "Instancia bloqueada. Todos los comentarios y cuentas serán rechazados inmediatamente. Todos los comentarios y cuentas pendientes de moderación fueron rechazados." + success: "Instancia bloqueada. A partir de ahora, todos los comentarios y cuentas serán rechazados inmediatamente. No se modificaron cuentas ni comentarios pre-existentes." error: "No se pudo bloquear la instancia. Hemos recibido el reporte y lo estaremos verificando." fediblock_states: action_on_several: - success: "Se habilitaron las listas de bloqueo, podés encontrar las instancias filtrando por Bloqueadas. Todas las cuentas de estas instancias pendientes de moderación han sido bloqueadas. Podés activarlas individualmente en la sección Cuentas." + success: "Se habilitaron las listas de bloqueo, podés encontrar las instancias filtrando por Bloqueadas. Podés activarlas individualmente en la sección Cuentas. No se modificaron cuentas ni comentarios pre-existentes." error: "Hubo un error al activar o desactivar listas de bloqueo, ya recibimos el reporte y lo estaremos verificando." - custom_blocklist_success: "Se agregaron las instancias personalizadas a la lista de bloqueo, podés encontrarlas filtrando por Bloqueadas. Todas las cuentas de estas instancias pendientes de moderación han sido bloqueadas. Podés aprobarlas individualmente en la sección Cuentas." + custom_blocklist_success: "Se agregaron las instancias personalizadas a la lista de bloqueo, podés encontrarlas filtrando por Bloqueadas. Podés aprobarlas individualmente en la sección Cuentas. No se modificaron cuentas ni comentarios pre-existentes." custom_blocklist_error: "Hubo un error al agregar instancias personalizadas a la lista de bloqueo, ya recibimos el reporte y lo estaremos verificando." moderation_queue: everything: 'Seleccionar todo' From 882ab1679c0467929e5cfc18bbf75402143b4752 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 13 Mar 2024 15:52:33 -0300 Subject: [PATCH 042/133] fix: a veces el objeto no existe (?) closes #15554 --- app/models/activity_pub.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/models/activity_pub.rb b/app/models/activity_pub.rb index a6b1401b..53bb3de0 100644 --- a/app/models/activity_pub.rb +++ b/app/models/activity_pub.rb @@ -56,6 +56,8 @@ class ActivityPub < ApplicationRecord transitions to: :removed before do + next if object.blank? + object.update(content: {}) unless object.content.empty? end end From 56c583e0f43efab5d4ea37a725fb8cb2801c30b8 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 13 Mar 2024 16:11:40 -0300 Subject: [PATCH 043/133] fix: serializar correctamente closes #15363 --- app/controllers/api/v1/webhooks/social_inbox_controller.rb | 5 +++-- app/jobs/activity_pub/fetch_job.rb | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/controllers/api/v1/webhooks/social_inbox_controller.rb b/app/controllers/api/v1/webhooks/social_inbox_controller.rb index c40857e0..bdf359d2 100644 --- a/app/controllers/api/v1/webhooks/social_inbox_controller.rb +++ b/app/controllers/api/v1/webhooks/social_inbox_controller.rb @@ -125,8 +125,9 @@ module Api o.save! # XXX: el objeto necesita ser guardado antes de poder - # procesarlo - ::ActivityPub::FetchJob.perform_later(site: site, object: o) unless object_embedded? + # procesarlo. No usamos GlobalID porque el tipo de objeto + # cambia y produce un error de deserialización. + ::ActivityPub::FetchJob.perform_later(site: site, object_id: o.id) unless object_embedded? end end diff --git a/app/jobs/activity_pub/fetch_job.rb b/app/jobs/activity_pub/fetch_job.rb index e3fef993..6a4d163b 100644 --- a/app/jobs/activity_pub/fetch_job.rb +++ b/app/jobs/activity_pub/fetch_job.rb @@ -11,8 +11,11 @@ class ActivityPub class FetchJob < ApplicationJob self.priority = 50 - def perform(site:, object:) + def perform(site:, object_id:) ActivityPub::Object.transaction do + object = ::ActivityPub::Object.find(object_id) + + return if object.blank? return if object.activity_pubs.where(aasm_state: 'removed').count.positive? response = site.social_inbox.dereferencer.get(uri: object.uri) From ad9f9c3d618eb7cba2834e350a03dd7c7804bd0c Mon Sep 17 00:00:00 2001 From: f Date: Wed, 13 Mar 2024 16:27:33 -0300 Subject: [PATCH 044/133] fixup! fix: serializar correctamente --- app/models/que_job.rb | 5 +++++ db/migrate/20240313192134_fix_fetch_jobs.rb | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 app/models/que_job.rb create mode 100644 db/migrate/20240313192134_fix_fetch_jobs.rb diff --git a/app/models/que_job.rb b/app/models/que_job.rb new file mode 100644 index 00000000..0bfffc92 --- /dev/null +++ b/app/models/que_job.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +require 'que/active_record/model' + +class QueJob < Que::ActiveRecord::Model; end diff --git a/db/migrate/20240313192134_fix_fetch_jobs.rb b/db/migrate/20240313192134_fix_fetch_jobs.rb new file mode 100644 index 00000000..3b9d4ad8 --- /dev/null +++ b/db/migrate/20240313192134_fix_fetch_jobs.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class FixFetchJobs < ActiveRecord::Migration[6.1] + def up + QueJob.where("last_error_message like '%ActiveJob::DeserializationError%'").find_each do |job| + job.error_count = 0 + job.run_at = time.now + + job.args.first['arguments'].first['_aj_ruby2_keywords'].delete('object') + job.args.first['arguments'].first['_aj_ruby2_keywords'] << 'object_id' + + object = job.args.first['arguments'].first.delete('object')['_aj_globalid'] + job.args.first['arguments'].first['object_id'] = object.split('/').last + + job.save + end + end + + def down; end +end From 630f6b31fc93021d5e3ee93447f3b980f3dd1510 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 13 Mar 2024 16:28:59 -0300 Subject: [PATCH 045/133] fixup! fixup! fix: serializar correctamente --- db/migrate/20240313192134_fix_fetch_jobs.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrate/20240313192134_fix_fetch_jobs.rb b/db/migrate/20240313192134_fix_fetch_jobs.rb index 3b9d4ad8..54ffa7e6 100644 --- a/db/migrate/20240313192134_fix_fetch_jobs.rb +++ b/db/migrate/20240313192134_fix_fetch_jobs.rb @@ -4,7 +4,7 @@ class FixFetchJobs < ActiveRecord::Migration[6.1] def up QueJob.where("last_error_message like '%ActiveJob::DeserializationError%'").find_each do |job| job.error_count = 0 - job.run_at = time.now + job.run_at = Time.now job.args.first['arguments'].first['_aj_ruby2_keywords'].delete('object') job.args.first['arguments'].first['_aj_ruby2_keywords'] << 'object_id' From 4dd75eedad7b8023a05bfb848697f71a8e575831 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 13 Mar 2024 16:39:58 -0300 Subject: [PATCH 046/133] =?UTF-8?q?fix:=20prevenir=20doble=20renderizaci?= =?UTF-8?q?=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes #15564 --- app/controllers/application_controller.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 05fa98e9..cfa37067 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -77,6 +77,11 @@ class ApplicationController < ActionController::Base # Muestra una página 404 def page_not_found + self.response_body = nil + @_response_body = nil + + headers.delete('Location') + render 'application/page_not_found', status: :not_found end From 061fc81cb058221cd237baa6646b8f67d0fc1843 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 13 Mar 2024 16:44:24 -0300 Subject: [PATCH 047/133] =?UTF-8?q?fixup!=20fix:=20prevenir=20doble=20rend?= =?UTF-8?q?erizaci=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/application_controller.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index cfa37067..b15157f7 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -76,6 +76,8 @@ class ApplicationController < ActionController::Base end # Muestra una página 404 + # + # @see {https://github.com/rails/rails/issues/25106} def page_not_found self.response_body = nil @_response_body = nil From 93140f37aa6ca1436cb42523ad1cd4865abba097 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 13 Mar 2024 16:46:04 -0300 Subject: [PATCH 048/133] fix: cachear correctamente closes #15567 --- app/views/moderation_queue/_comments.haml | 6 +++--- app/views/moderation_queue/_instances.haml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/views/moderation_queue/_comments.haml b/app/views/moderation_queue/_comments.haml index 72240287..a4bfd9bd 100644 --- a/app/views/moderation_queue/_comments.haml +++ b/app/views/moderation_queue/_comments.haml @@ -12,6 +12,6 @@ - if moderation_queue.count.zero? %h4= t('moderation_queue.nothing') - moderation_queue.each do |activity_pub| - -# cache [activity_pub, activity_pub.object, activity_pub.actor] do - %hr - = render 'moderation_queue/comment', comment: activity_pub.object.content, profile: activity_pub.actor.content, activity_pub: activity_pub, form: form_id + - cache [activity_pub, activity_pub.object, activity_pub.actor] do + %hr + = render 'moderation_queue/comment', comment: activity_pub.object.content, profile: activity_pub.actor.content, activity_pub: activity_pub, form: form_id diff --git a/app/views/moderation_queue/_instances.haml b/app/views/moderation_queue/_instances.haml index 9a5349ba..dec7e6f3 100644 --- a/app/views/moderation_queue/_instances.haml +++ b/app/views/moderation_queue/_instances.haml @@ -14,7 +14,7 @@ %h4= t('moderation_queue.nothing') - instance_moderations.each do |instance_moderation| - - cache [instance_moderation.aasm_state, instance_moderation.instance] do + - cache [instance_moderation, instance_moderation.instance] do %hr = render 'moderation_queue/instance', instance_moderation: instance_moderation, instance: instance_moderation.instance, form: form_id From ac38343a219953038540e3ecea460516f998fb54 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 13 Mar 2024 16:55:34 -0300 Subject: [PATCH 049/133] =?UTF-8?q?fix:=20facilitar=20la=20b=C3=BAsqueda?= =?UTF-8?q?=20de=20instancias=20en=20la=20lista=20#15563?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/views/moderation_queue/_instance.haml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/views/moderation_queue/_instance.haml b/app/views/moderation_queue/_instance.haml index 7cf3b085..e5121dad 100644 --- a/app/views/moderation_queue/_instance.haml +++ b/app/views/moderation_queue/_instance.haml @@ -1,12 +1,17 @@ - usuaries = instance.content.dig('usage', 'users', 'active_month') - usuaries ||= instance.content.dig('stats', 'user_count') +- title = sanitize(instance.content['title']) +- title ||= instance.hostname .row.no-gutters.pt-2 .col-1 = render 'components/checkbox', id: instance.hostname, form: form, name: 'instance_moderation[]', value: instance_moderation.id, data: { target: 'select-all.input' } .col-11 %h4 - %a{ href: instance.uri }= sanitize(instance.content['title']) || instance.hostname + %a{ href: instance.uri } + = title + - if title.present? + = " (#{instance.hostname})".html_safe .content = sanitize instance.content['description'] - if usuaries.present? From 79e6ce787274aa928b2c726f94342597a74812bc Mon Sep 17 00:00:00 2001 From: f Date: Wed, 13 Mar 2024 16:56:51 -0300 Subject: [PATCH 050/133] =?UTF-8?q?fixup!=20fix:=20facilitar=20la=20b?= =?UTF-8?q?=C3=BAsqueda=20de=20instancias=20en=20la=20lista=20#15563?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/views/moderation_queue/_instance.haml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/views/moderation_queue/_instance.haml b/app/views/moderation_queue/_instance.haml index e5121dad..d66acd83 100644 --- a/app/views/moderation_queue/_instance.haml +++ b/app/views/moderation_queue/_instance.haml @@ -1,15 +1,13 @@ - usuaries = instance.content.dig('usage', 'users', 'active_month') - usuaries ||= instance.content.dig('stats', 'user_count') -- title = sanitize(instance.content['title']) -- title ||= instance.hostname +- title = sanitize(instance.content['title']) .row.no-gutters.pt-2 .col-1 = render 'components/checkbox', id: instance.hostname, form: form, name: 'instance_moderation[]', value: instance_moderation.id, data: { target: 'select-all.input' } .col-11 %h4 - %a{ href: instance.uri } - = title + %a{ href: instance.uri }= title || instance.hostname - if title.present? = " (#{instance.hostname})".html_safe .content From 97653d98b6d3f0d2d667eb153f5e70e8b9b272d1 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 13 Mar 2024 17:02:01 -0300 Subject: [PATCH 051/133] fix: prevenir deadlocks closes #15555 closes #15556 closes #15557 closes #15558 closes #15559 closes #15560 --- app/models/activity_pub/activity/delete.rb | 26 ++++++++++++---------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/app/models/activity_pub/activity/delete.rb b/app/models/activity_pub/activity/delete.rb index 6a23a8b5..1fbe5691 100644 --- a/app/models/activity_pub/activity/delete.rb +++ b/app/models/activity_pub/activity/delete.rb @@ -13,22 +13,24 @@ class ActivityPub # lo haría la Social Inbox por nosotres. # @see {https://docs.joinmastodon.org/spec/security/#ld} def update_activity_pub_state! - ActivityPub.transaction do - object = ActivityPub::Object.find_by(uri: ActivityPub.uri_from_object(content['object'])) + ActiveRecord::Base.connection_pool.with_connection do + ActivityPub.transaction do + object = ActivityPub::Object.find_by(uri: ActivityPub.uri_from_object(content['object'])) - if object.present? - object.activity_pubs.find_each do |activity_pub| - activity_pub.remove! if activity_pub.may_remove? + if object.present? + object.activity_pubs.find_each do |activity_pub| + activity_pub.remove! if activity_pub.may_remove? + end + + # Encontrar todas las acciones de moderación de le actore + # eliminade y moverlas a eliminar. + if object.actor_type? && object.actor.present? + ActorModeration.where(actor_id: object.actor.id).remove_all! + end end - # Encontrar todas las acciones de moderación de le actore - # eliminade y moverlas a eliminar. - if object.actor_type? && object.actor.present? - ActorModeration.where(actor_id: object.actor.id).remove_all! - end + activity_pub.remove! if activity_pub.may_remove? end - - activity_pub.remove! if activity_pub.may_remove? end end end From 1b9e039fda52d2b1ee5779823b2ec7cb00873980 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 13 Mar 2024 17:42:36 -0300 Subject: [PATCH 052/133] =?UTF-8?q?fix:=20errores=20de=20decompresi=C3=B3n?= =?UTF-8?q?=20#15332?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Gemfile | 2 +- Gemfile.lock | 2 +- ...04105_brs_decompressor_corrupted_source_error.rb | 13 +++++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20240313204105_brs_decompressor_corrupted_source_error.rb diff --git a/Gemfile b/Gemfile index f4125f65..e314c172 100644 --- a/Gemfile +++ b/Gemfile @@ -39,7 +39,7 @@ gem 'devise-i18n' gem 'devise_invitable' gem 'redis-client' gem 'hiredis-client' -gem 'distributed-press-api-client', '~> 0.4.0rc3' +gem 'distributed-press-api-client', '~> 0.4.1' gem 'email_address', git: 'https://github.com/fauno/email_address', branch: 'i18n' gem 'exception_notification' gem 'fast_blank' diff --git a/Gemfile.lock b/Gemfile.lock index 7b19ba75..751fc73e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -166,7 +166,7 @@ GEM devise_invitable (2.0.9) actionmailer (>= 5.0) devise (>= 4.6) - distributed-press-api-client (0.4.0) + distributed-press-api-client (0.4.1) addressable (~> 2.3, >= 2.3.0) climate_control dry-schema diff --git a/db/migrate/20240313204105_brs_decompressor_corrupted_source_error.rb b/db/migrate/20240313204105_brs_decompressor_corrupted_source_error.rb new file mode 100644 index 00000000..a0c29311 --- /dev/null +++ b/db/migrate/20240313204105_brs_decompressor_corrupted_source_error.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +# Comprueba que se pueden volver a correr las tareas que dieron error de +# decompresión +class BrsDecompressorCorruptedSourceError < ActiveRecord::Migration[6.1] + def up + raise unless HTTParty.get("https://mas.to/api/v2/instance", headers: { "Accept-Encoding": "br;q=1.0,gzip;q=1.0,deflate;q=0.6,identity;q=0.3" }).ok? + + QueJob.where("last_error_message like '%BRS::DecompressorCorruptedSourceError%'").update_all(error_count: 0, run_at: Time.now) + end + + def down; end +end From b6ed91df11eb238d55f6aedb95e164512046bfa7 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 13 Mar 2024 17:52:45 -0300 Subject: [PATCH 053/133] fix: siempre relacionar la instancia con el sitio #15563 --- app/controllers/api/v1/webhooks/social_inbox_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/v1/webhooks/social_inbox_controller.rb b/app/controllers/api/v1/webhooks/social_inbox_controller.rb index bdf359d2..7b17c47b 100644 --- a/app/controllers/api/v1/webhooks/social_inbox_controller.rb +++ b/app/controllers/api/v1/webhooks/social_inbox_controller.rb @@ -162,11 +162,11 @@ module Api unless a.instance a.instance = ::ActivityPub::Instance.find_or_create_by(hostname: URI.parse(a.uri).hostname) - site.instance_moderations.find_or_create_by(instance: a.instance) - ::ActivityPub::InstanceFetchJob.perform_later(site: site, instance: a.instance) end + site.instance_moderations.find_or_create_by(instance: a.instance) + a.save! site.actor_moderations.find_or_create_by(actor: a) From 9d71c5fa0cb4de8725a1b6728cbb71a0edc087e2 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 14 Mar 2024 11:20:04 -0300 Subject: [PATCH 054/133] fix: eliminar actores cuando no se pudo completar el objeto #15576 --- app/models/activity_pub/activity/delete.rb | 4 ++-- ...emove_actor_moderations_for_generic_objects.rb | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20240314141536_remove_actor_moderations_for_generic_objects.rb diff --git a/app/models/activity_pub/activity/delete.rb b/app/models/activity_pub/activity/delete.rb index 1fbe5691..997b96ca 100644 --- a/app/models/activity_pub/activity/delete.rb +++ b/app/models/activity_pub/activity/delete.rb @@ -24,8 +24,8 @@ class ActivityPub # Encontrar todas las acciones de moderación de le actore # eliminade y moverlas a eliminar. - if object.actor_type? && object.actor.present? - ActorModeration.where(actor_id: object.actor.id).remove_all! + if (actor = ActivityPub::Actor.find_by(uri: o.uri)).present? + ActorModeration.where(actor_id: actor.id).remove_all! end end diff --git a/db/migrate/20240314141536_remove_actor_moderations_for_generic_objects.rb b/db/migrate/20240314141536_remove_actor_moderations_for_generic_objects.rb new file mode 100644 index 00000000..a60e755a --- /dev/null +++ b/db/migrate/20240314141536_remove_actor_moderations_for_generic_objects.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +# Elimina actores que no pudieron ser eliminades porque su perfil ya no +# existe. +class RemoveActorModerationsForGenericObjects < ActiveRecord::Migration[6.1] + def up + object_ids = ActivityPub.removed.where(object_type: 'ActivityPub::Object::Generic').distinct.pluck(:object_id) + uris = ActivityPub::Object.where(id: object_ids).pluck(:uri) + actor_ids = ActivityPub::Actor.where(uri: uris).ids + + ActorModeration.where(actor_id: actor_ids).remove_all! + end + + def down; end +end From 30f2f0a4423e623ba79ef385d3cba23e17a12edf Mon Sep 17 00:00:00 2001 From: f Date: Thu, 14 Mar 2024 11:45:33 -0300 Subject: [PATCH 055/133] fix: gestionar las excepciones desde un solo lugar #15564 --- app/controllers/application_controller.rb | 16 --------------- app/controllers/concerns/exception_handler.rb | 20 ++++++++++++++++++- config/locales/en.yml | 1 + config/locales/es.yml | 1 + 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index b15157f7..bd7b8c4b 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -12,10 +12,6 @@ class ApplicationController < ActionController::Base before_action :notify_unconfirmed_email, unless: :devise_controller? around_action :set_locale - rescue_from Pundit::NilPolicyError, with: :page_not_found - rescue_from ActionController::RoutingError, with: :page_not_found - rescue_from ActionController::ParameterMissing, with: :page_not_found - before_action do Rack::MiniProfiler.authorize_request if current_usuarie&.email&.ends_with?('@' + ENV.fetch('SUTTY', 'sutty.nl')) end @@ -75,18 +71,6 @@ class ApplicationController < ActionController::Base I18n.with_locale(current_locale, &action) end - # Muestra una página 404 - # - # @see {https://github.com/rails/rails/issues/25106} - def page_not_found - self.response_body = nil - @_response_body = nil - - headers.delete('Location') - - render 'application/page_not_found', status: :not_found - end - # Necesario para poder acceder a Blazer. Solo les usuaries de este # sitio pueden acceder al panel. def require_usuarie diff --git a/app/controllers/concerns/exception_handler.rb b/app/controllers/concerns/exception_handler.rb index 8c4f54c8..7c1cd540 100644 --- a/app/controllers/concerns/exception_handler.rb +++ b/app/controllers/concerns/exception_handler.rb @@ -12,13 +12,31 @@ module ExceptionHandler rescue_from PageNotFound, with: :page_not_found rescue_from ActionController::RoutingError, with: :page_not_found rescue_from Pundit::NilPolicyError, with: :page_not_found + rescue_from Pundit::NilPolicyError, with: :page_not_found + rescue_from ActionController::RoutingError, with: :page_not_found + rescue_from ActionController::ParameterMissing, with: :page_not_found end def site_not_found + reset_response! + + flash[:error] = I18n.t('errors.site_not_found') + redirect_to sites_path end def page_not_found - send_file Rails.root.join('public', '404.html') + reset_response! + + render 'application/page_not_found', status: :not_found + end + + private + + def reset_response! + self.response_body = nil + @_response_body = nil + + headers.delete('Location') end end diff --git a/config/locales/en.yml b/config/locales/en.yml index d745e2d5..d67fe7d3 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -390,6 +390,7 @@ en: lang: not_available: "This language is not yet available, would you help us by translating Sutty into it?" errors: + site_not_found: "Site not found, or maybe you don't have access to it." argument_error: 'Argument `%{argument}` must be an instance of %{class}' unknown_locale: 'Unknown %{locale} locale' posts: diff --git a/config/locales/es.yml b/config/locales/es.yml index a7b8d452..192b3298 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -389,6 +389,7 @@ es: lang: not_available: "Este idioma todavía no está disponible, ¿nos ayudas a agregarlo y mantenerlo?" errors: + site_not_found: "No encontramos ese sitio o quizás no tengas acceso." argument_error: 'El argumento `%{argument}` debe ser una instancia de %{class}' unknown_locale: 'El idioma %{locale} es desconocido' posts: From cfe70e3a899f136a2a2e004c5ae7ef91681fd4a2 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 14 Mar 2024 11:57:06 -0300 Subject: [PATCH 056/133] fix: no cachear formularios #15570 --- app/views/moderation_queue/_account.haml | 14 ++++----- app/views/moderation_queue/_accounts.haml | 5 ++-- app/views/moderation_queue/_comment.haml | 35 +++++++++++----------- app/views/moderation_queue/_comments.haml | 5 ++-- app/views/moderation_queue/_instance.haml | 21 ++++++------- app/views/moderation_queue/_instances.haml | 5 ++-- 6 files changed, 42 insertions(+), 43 deletions(-) diff --git a/app/views/moderation_queue/_account.haml b/app/views/moderation_queue/_account.haml index 6b4c67fc..498d78f4 100644 --- a/app/views/moderation_queue/_account.haml +++ b/app/views/moderation_queue/_account.haml @@ -5,12 +5,12 @@ .col-1 = render 'components/checkbox', id: actor_moderation.id, form: form, name: 'actor_moderation[]', value: actor_moderation.id, data: { target: 'select-all.input' } .col-11 - %h4 - = link_to text_plain(profile['name']), site_actor_moderation_path(id: actor_moderation) - .mb-3 - = sanitize profile['summary'] + - cache [actor_moderation, profile] do + %h4 + = link_to text_plain(profile['name']), site_actor_moderation_path(id: actor_moderation) + .mb-3 + = sanitize profile['summary'] -# Botones de Moderación - - cache actor_moderation do - .d-flex.pb-4 - = render 'components/profiles_btn_box', actor_moderation: actor_moderation + .d-flex.pb-4 + = render 'components/profiles_btn_box', actor_moderation: actor_moderation diff --git a/app/views/moderation_queue/_accounts.haml b/app/views/moderation_queue/_accounts.haml index abc02b31..d8d76f0d 100644 --- a/app/views/moderation_queue/_accounts.haml +++ b/app/views/moderation_queue/_accounts.haml @@ -12,6 +12,5 @@ - if actor_moderations.count.zero? %h4= t('moderation_queue.nothing') - actor_moderations.find_each do |actor_moderation| - - cache [actor_moderation, actor_moderation.actor] do - %hr - = render 'account', actor_moderation: actor_moderation, profile: actor_moderation.actor.content, form: form_id + %hr + = render 'account', actor_moderation: actor_moderation, profile: actor_moderation.actor.content, form: form_id diff --git a/app/views/moderation_queue/_comment.haml b/app/views/moderation_queue/_comment.haml index 90579a9c..c6f6fd5c 100644 --- a/app/views/moderation_queue/_comment.haml +++ b/app/views/moderation_queue/_comment.haml @@ -13,22 +13,23 @@ .col-1 = render 'components/checkbox', id: activity_pub.id, name: 'activity_pub[]', value: activity_pub.id, data: { target: 'select-all.input' }, form: form .col-11 - .d-flex.flex-row.align-items-center.justify-content-between - %h4.mb-0 - %a{ href: text_plain(comment['attributedTo']) }= text_plain profile['preferredUsername'] - %small - = render 'layouts/time', time: text_plain(comment['published']) - - if in_reply_to.present? - %dl - %dt.d-inline - %small= t('.reply_to') - %dd.d-inline - %small - %a{ href: in_reply_to }= in_reply_to - .content - - if summary.present? - = render 'layouts/details', summary: summary, summary_class: 'h5' do + - cache [activity_pub, comment] do + .d-flex.flex-row.align-items-center.justify-content-between + %h4.mb-0 + %a{ href: text_plain(comment['attributedTo']) }= text_plain profile['preferredUsername'] + %small + = render 'layouts/time', time: text_plain(comment['published']) + - if in_reply_to.present? + %dl + %dt.d-inline + %small= t('.reply_to') + %dd.d-inline + %small + %a{ href: in_reply_to }= in_reply_to + .content + - if summary.present? + = render 'layouts/details', summary: summary, summary_class: 'h5' do + = sanitize comment['content'] + - else = sanitize comment['content'] - - else - = sanitize comment['content'] = render 'components/comments_btn_box', activity_pub: activity_pub diff --git a/app/views/moderation_queue/_comments.haml b/app/views/moderation_queue/_comments.haml index a4bfd9bd..68671f9e 100644 --- a/app/views/moderation_queue/_comments.haml +++ b/app/views/moderation_queue/_comments.haml @@ -12,6 +12,5 @@ - if moderation_queue.count.zero? %h4= t('moderation_queue.nothing') - moderation_queue.each do |activity_pub| - - cache [activity_pub, activity_pub.object, activity_pub.actor] do - %hr - = render 'moderation_queue/comment', comment: activity_pub.object.content, profile: activity_pub.actor.content, activity_pub: activity_pub, form: form_id + %hr + = render 'moderation_queue/comment', comment: activity_pub.object.content, profile: activity_pub.actor.content, activity_pub: activity_pub, form: form_id diff --git a/app/views/moderation_queue/_instance.haml b/app/views/moderation_queue/_instance.haml index d66acd83..c380089a 100644 --- a/app/views/moderation_queue/_instance.haml +++ b/app/views/moderation_queue/_instance.haml @@ -6,16 +6,17 @@ .col-1 = render 'components/checkbox', id: instance.hostname, form: form, name: 'instance_moderation[]', value: instance_moderation.id, data: { target: 'select-all.input' } .col-11 - %h4 - %a{ href: instance.uri }= title || instance.hostname - - if title.present? - = " (#{instance.hostname})".html_safe - .content - = sanitize instance.content['description'] - - if usuaries.present? - %dl - %dt.d-inline= t('.users') - %dd.d-inline= text_plain usuaries.to_s + - cache [instance_moderation, instance] do + %h4 + %a{ href: instance.uri }= title || instance.hostname + - if title.present? + = " (#{instance.hostname})".html_safe + .content + = sanitize instance.content['description'] + - if usuaries.present? + %dl + %dt.d-inline= t('.users') + %dd.d-inline= text_plain usuaries.to_s -# Botones moderación .d-flex.pb-4 diff --git a/app/views/moderation_queue/_instances.haml b/app/views/moderation_queue/_instances.haml index dec7e6f3..6bc08b95 100644 --- a/app/views/moderation_queue/_instances.haml +++ b/app/views/moderation_queue/_instances.haml @@ -14,9 +14,8 @@ %h4= t('moderation_queue.nothing') - instance_moderations.each do |instance_moderation| - - cache [instance_moderation, instance_moderation.instance] do - %hr - = render 'moderation_queue/instance', instance_moderation: instance_moderation, instance: instance_moderation.instance, form: form_id + %hr + = render 'moderation_queue/instance', instance_moderation: instance_moderation, instance: instance_moderation.instance, form: form_id %hr %div From c57794b54e92cb6eed8dd42944b320aa9c513d7f Mon Sep 17 00:00:00 2001 From: f Date: Thu, 14 Mar 2024 11:58:13 -0300 Subject: [PATCH 057/133] fixup! fix: eliminar actores cuando no se pudo completar el objeto #15576 --- app/models/activity_pub/activity/delete.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/activity_pub/activity/delete.rb b/app/models/activity_pub/activity/delete.rb index 997b96ca..640c7ce9 100644 --- a/app/models/activity_pub/activity/delete.rb +++ b/app/models/activity_pub/activity/delete.rb @@ -24,7 +24,7 @@ class ActivityPub # Encontrar todas las acciones de moderación de le actore # eliminade y moverlas a eliminar. - if (actor = ActivityPub::Actor.find_by(uri: o.uri)).present? + if (actor = ActivityPub::Actor.find_by(uri: object.uri)).present? ActorModeration.where(actor_id: actor.id).remove_all! end end From 1de9d1a12695d97679c64b403c32256a4abf6ec1 Mon Sep 17 00:00:00 2001 From: maki Date: Thu, 14 Mar 2024 12:02:37 -0300 Subject: [PATCH 058/133] =?UTF-8?q?feat:=20breadcrumbs=20en=20Actividades?= =?UTF-8?q?=20de=20Moderaci=C3=B3n=20#15578?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/moderation_queue_controller.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/controllers/moderation_queue_controller.rb b/app/controllers/moderation_queue_controller.rb index eec0c70f..ca99d95b 100644 --- a/app/controllers/moderation_queue_controller.rb +++ b/app/controllers/moderation_queue_controller.rb @@ -2,6 +2,11 @@ # Cola de moderación de ActivityPub class ModerationQueueController < ApplicationController + before_action :authenticate_usuarie! + + breadcrumb -> { current_usuarie.email }, :edit_usuarie_registration_path + breadcrumb 'sites.index', :sites_path, match: :exact + # Cola de moderación viendo todo el sitio def index dummy_data From 33cfdd199513221c8b4bf30f64fbe0b454c4be7a Mon Sep 17 00:00:00 2001 From: maki Date: Thu, 14 Mar 2024 12:21:50 -0300 Subject: [PATCH 059/133] fix: breadcrumbs en todas las acciones --- app/controllers/moderation_queue_controller.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/controllers/moderation_queue_controller.rb b/app/controllers/moderation_queue_controller.rb index ca99d95b..cbf2cec5 100644 --- a/app/controllers/moderation_queue_controller.rb +++ b/app/controllers/moderation_queue_controller.rb @@ -6,7 +6,7 @@ class ModerationQueueController < ApplicationController breadcrumb -> { current_usuarie.email }, :edit_usuarie_registration_path breadcrumb 'sites.index', :sites_path, match: :exact - + # Cola de moderación viendo todo el sitio def index dummy_data @@ -15,11 +15,13 @@ class ModerationQueueController < ApplicationController # Perfil remoto de usuarie def remote_profile dummy_data + breadcrumb post.title.value, '' end # todon.nl está usando /api/v2/instance # mauve.moe usa /api/v1/instance def instances dummy_data + breadcrumb post.title.value, '' end end From 5bf26a7fb2f7b7fb73efd11f110ea8ec6bb44383 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 14 Mar 2024 12:27:59 -0300 Subject: [PATCH 060/133] fix: arreglar las relaciones entre actividades y objetos --- app/jobs/activity_pub/fetch_job.rb | 4 ++++ ...0240314153017_fix_object_type_on_activity_pubs.rb | 12 ++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 db/migrate/20240314153017_fix_object_type_on_activity_pubs.rb diff --git a/app/jobs/activity_pub/fetch_job.rb b/app/jobs/activity_pub/fetch_job.rb index 6a4d163b..d7003c11 100644 --- a/app/jobs/activity_pub/fetch_job.rb +++ b/app/jobs/activity_pub/fetch_job.rb @@ -24,9 +24,13 @@ class ActivityPub return unless response.ok? return if response.miss? && object.content.present? + current_type = object.type content = FastJsonparser.parse(response.body) object.update(content: content, type: ActivityPub::Object.type_from(content).name) + + # Arreglar las relaciones con actividades también + ActivityPub.where(object_id: object.id).update_all(object_type: object.type) unless current_type == object.type end end end diff --git a/db/migrate/20240314153017_fix_object_type_on_activity_pubs.rb b/db/migrate/20240314153017_fix_object_type_on_activity_pubs.rb new file mode 100644 index 00000000..81149ad4 --- /dev/null +++ b/db/migrate/20240314153017_fix_object_type_on_activity_pubs.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +# Arregla la relación rota entre ActivityPub y Objects +class FixObjectTypeOnActivityPubs < ActiveRecord::Migration[6.1] + def up + ActivityPub::Object.where.not(type: 'ActivityPub::Object::Generic').find_each do |object| + ActivityPub.where(object_id: object.id).update_all(type: object.type, updated_at: Time.now) + end + end + + def down; end +end From 5969662a7d679836c1936a178ee097641fdfeec6 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 14 Mar 2024 15:42:44 -0300 Subject: [PATCH 061/133] fixup! fix: arreglar las relaciones entre actividades y objetos --- db/migrate/20240314153017_fix_object_type_on_activity_pubs.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrate/20240314153017_fix_object_type_on_activity_pubs.rb b/db/migrate/20240314153017_fix_object_type_on_activity_pubs.rb index 81149ad4..d5475f71 100644 --- a/db/migrate/20240314153017_fix_object_type_on_activity_pubs.rb +++ b/db/migrate/20240314153017_fix_object_type_on_activity_pubs.rb @@ -4,7 +4,7 @@ class FixObjectTypeOnActivityPubs < ActiveRecord::Migration[6.1] def up ActivityPub::Object.where.not(type: 'ActivityPub::Object::Generic').find_each do |object| - ActivityPub.where(object_id: object.id).update_all(type: object.type, updated_at: Time.now) + ActivityPub.where(object_id: object.id).update_all(object_type: object.type, updated_at: Time.now) end end From 96d70100e46ad536d5e017b215f559a7a0615380 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 14 Mar 2024 17:07:58 -0300 Subject: [PATCH 062/133] feat: auto-aprobar solicitudes de seguimiento --- app/models/activity_pub/activity/follow.rb | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/models/activity_pub/activity/follow.rb b/app/models/activity_pub/activity/follow.rb index e383490a..393eb3b4 100644 --- a/app/models/activity_pub/activity/follow.rb +++ b/app/models/activity_pub/activity/follow.rb @@ -4,8 +4,17 @@ # # Una actividad de seguimiento se refiere siempre a une actore (el # sitio) y proviene de otre actore. +# +# Por ahora las solicitudes de seguimiento se auto-aprueban. class ActivityPub class Activity - class Follow < ActivityPub::Activity; end + class Follow < ActivityPub::Activity + # Auto-aprobar la solicitud de seguimiento + def update_activity_pub_state! + activity_pub.approve! + rescue Exception => e + ExceptionNotifier.notify_exception(e, { site: activity_pub.site.name, activity: self.id }) + end + end end end From d92953e5fa3acb9bc1e065dcd5f25d31b326e781 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 14 Mar 2024 17:08:38 -0300 Subject: [PATCH 063/133] fix: poder volver a pausa si se edita el comentario --- app/models/activity_pub.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/models/activity_pub.rb b/app/models/activity_pub.rb index 53bb3de0..7453a8c3 100644 --- a/app/models/activity_pub.rb +++ b/app/models/activity_pub.rb @@ -49,6 +49,12 @@ class ActivityPub < ApplicationRecord # Le actore eliminó el objeto state :removed + # Se puede volver a pausa en caso de actualización remota, para + # revisar los cambios. + event :pause do + transitions to: :paused + end + # Recibir una acción de eliminación, eliminar el contenido de la # base de datos. Esto elimina el contenido para todos los sitios # porque estamos respetando lo que pidió le actore. From 1e44fba4b3b70a033bf7026d12ea2da9c3329be8 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 14 Mar 2024 17:08:55 -0300 Subject: [PATCH 064/133] fix: aprobar la actividad, no el objeto --- app/models/activity_pub.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/models/activity_pub.rb b/app/models/activity_pub.rb index 7453a8c3..bd6c816e 100644 --- a/app/models/activity_pub.rb +++ b/app/models/activity_pub.rb @@ -101,11 +101,14 @@ class ActivityPub < ApplicationRecord # Definir eventos en masa include AasmEventsConcern + # Lo que tenemos que aprobar o rechazar es la última actividad + # disponible, que según el scope por defecto, va a ser la primera de + # la lista. def reject_remotely! - raise unless site.social_inbox.inbox.reject(id: object.uri).ok? + raise unless site.social_inbox.inbox.reject(id: activities.first.uri).ok? end def allow_remotely! - raise unless site.social_inbox.inbox.accept(id: object.uri).ok? + raise unless site.social_inbox.inbox.accept(id: activities.first.uri).ok? end end From f4379b45c27583c757dd9b1683e705fdbaf382d8 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 14 Mar 2024 17:58:41 -0300 Subject: [PATCH 065/133] feat: announce y like --- app/models/activity_pub/activity/announce.rb | 8 ++++++++ app/models/activity_pub/activity/like.rb | 8 ++++++++ db/migrate/20240314205923_fix_activity_type.rb | 12 ++++++++++++ 3 files changed, 28 insertions(+) create mode 100644 app/models/activity_pub/activity/announce.rb create mode 100644 app/models/activity_pub/activity/like.rb create mode 100644 db/migrate/20240314205923_fix_activity_type.rb diff --git a/app/models/activity_pub/activity/announce.rb b/app/models/activity_pub/activity/announce.rb new file mode 100644 index 00000000..8ca58906 --- /dev/null +++ b/app/models/activity_pub/activity/announce.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class ActivityPub + class Activity + # Boost + class Announce < ActivityPub::Activity; end + end +end diff --git a/app/models/activity_pub/activity/like.rb b/app/models/activity_pub/activity/like.rb new file mode 100644 index 00000000..531cc32c --- /dev/null +++ b/app/models/activity_pub/activity/like.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class ActivityPub + class Activity + # Like + class Like < ActivityPub::Activity; end + end +end diff --git a/db/migrate/20240314205923_fix_activity_type.rb b/db/migrate/20240314205923_fix_activity_type.rb new file mode 100644 index 00000000..042de8eb --- /dev/null +++ b/db/migrate/20240314205923_fix_activity_type.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +# Soportar nuevos tipos +class FixActivityType < ActiveRecord::Migration[6.1] + def up + %w[Like Announce].each do |type| + ActivityPub::Activity.where(Arel.sql("content->>'type' = '#{type}'")).update_all(type: "ActivityPub::Activity::#{type}", updated_at: Time.now) + end + end + + def down; end +end From 8473927ceed6ea7dc8d01c30856499fcd200e998 Mon Sep 17 00:00:00 2001 From: maki Date: Fri, 15 Mar 2024 12:23:04 -0300 Subject: [PATCH 066/133] fix: breadcrumbs --- app/controllers/moderation_queue_controller.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/controllers/moderation_queue_controller.rb b/app/controllers/moderation_queue_controller.rb index cbf2cec5..5e501329 100644 --- a/app/controllers/moderation_queue_controller.rb +++ b/app/controllers/moderation_queue_controller.rb @@ -10,18 +10,17 @@ class ModerationQueueController < ApplicationController # Cola de moderación viendo todo el sitio def index dummy_data + breadcrumb I18n.t('moderation_queue.index.title'), '' end # Perfil remoto de usuarie def remote_profile dummy_data - breadcrumb post.title.value, '' end # todon.nl está usando /api/v2/instance # mauve.moe usa /api/v1/instance def instances dummy_data - breadcrumb post.title.value, '' end end From 30d32a6e3be6e3fd27ddfe81fa3eeda935f29211 Mon Sep 17 00:00:00 2001 From: f Date: Fri, 15 Mar 2024 13:43:00 -0300 Subject: [PATCH 067/133] feat: confirmar el reporte remoto #15604 --- app/views/components/_btn_base.haml | 2 +- app/views/components/_comments_btn_box.haml | 5 ++++- app/views/components/_instances_btn_box.haml | 12 ++++++++---- app/views/components/_profiles_btn_box.haml | 7 ++++--- config/locales/en.yml | 6 ++++-- config/locales/es.yml | 4 +++- 6 files changed, 24 insertions(+), 12 deletions(-) diff --git a/app/views/components/_btn_base.haml b/app/views/components/_btn_base.haml index 4d8566d3..f9227482 100644 --- a/app/views/components/_btn_base.haml +++ b/app/views/components/_btn_base.haml @@ -5,5 +5,5 @@ - local_assigns[:class] = "btn #{local_assigns[:class]}" -# @todo path es obligatorio -= button_to local_assigns[:path], **local_assigns do += button_to local_assigns[:path], **local_assigns.compact do = text diff --git a/app/views/components/_comments_btn_box.haml b/app/views/components/_comments_btn_box.haml index 285eefdb..1993e5cb 100644 --- a/app/views/components/_comments_btn_box.haml +++ b/app/views/components/_comments_btn_box.haml @@ -1,8 +1,11 @@ -# Componente Botonera de Comentarios +- local_data = { report: { confirm: t('.confirm_report') } } + .d-flex.flex-row - ActivityPub.events.each do |event| = render 'components/btn_base', text: t(".text_#{event}"), path: public_send(:"site_activity_pub_#{event}_path", activity_pub_id: activity_pub), - disabled: !activity_pub.public_send(:"may_#{event}?") + disabled: !activity_pub.public_send(:"may_#{event}?"), + data: local_data[event] diff --git a/app/views/components/_instances_btn_box.haml b/app/views/components/_instances_btn_box.haml index 74cad4a4..15c6c040 100644 --- a/app/views/components/_instances_btn_box.haml +++ b/app/views/components/_instances_btn_box.haml @@ -1,6 +1,10 @@ -# Componente botonera de moderación de Instancias -- btn_class = 'btn btn-secondary' -= render 'components/btn_base', path: site_instance_moderation_pause_path(instance_moderation_id: instance_moderation), text: t('.text_check'), class: btn_class, disabled: !instance_moderation.may_pause? -= render 'components/btn_base', path: site_instance_moderation_allow_path(instance_moderation_id: instance_moderation), text: t('.text_allow'), class: btn_class, disabled: !instance_moderation.may_allow? -= render 'components/btn_base', path: site_instance_moderation_block_path(instance_moderation_id: instance_moderation), text: t('.text_deny'), class: btn_class, disabled: !instance_moderation.may_block? +- local_data = {} +- InstanceModeration.events.each do |event| + = render 'components/btn_base', + path: public_send(:"site_instance_moderation_#{event}_path", instance_moderation_id: instance_moderation), + text: t(".text_#{event}"), + class: 'btn btn-secondary', + disabled: !instance_moderation.public_send(:"may_#{event}?"), + data: local_data[event] diff --git a/app/views/components/_profiles_btn_box.haml b/app/views/components/_profiles_btn_box.haml index 073c142e..488373b9 100644 --- a/app/views/components/_profiles_btn_box.haml +++ b/app/views/components/_profiles_btn_box.haml @@ -1,9 +1,10 @@ -# Componente Botonera de Moderación de Cuentas (Remote_profile) .d-flex.flex-row - - btn_class = 'btn-secondary' + - local_data = { report: { confirm: t('.confirm_report') } } - ActorModeration.events.each do |actor_event| = render 'components/btn_base', text: t(".text_#{actor_event}"), path: public_send(:"site_actor_moderation_#{actor_event}_path", actor_moderation_id: actor_moderation), - class: btn_class, - disabled: !actor_moderation.public_send(:"may_#{actor_event}?") + class: 'btn-secondary', + disabled: !actor_moderation.public_send(:"may_#{actor_event}?"), + data: local_data[actor_event] diff --git a/config/locales/en.yml b/config/locales/en.yml index d67fe7d3..6f76fe57 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -106,15 +106,17 @@ en: text_reject: Reject text_reply: Reply text_report: Report + confirm_report: "Send report to the remote instance?" instances_btn_box: - text_check: Check case by case + text_pause: Check case by case text_allow: Allow everything - text_deny: Block instance + text_block: Block instance profiles_btn_box: text_pause: Always check text_allow: Always approve text_block: Block text_report: Report + confirm_report: "Send report to the remote instance?" remote_flags: report_message: "Hi! Someone using Sutty CMS reported this account on your instance. We don't have support for customized report messages yet, but we will soon. You can reach us at %{panel_actor_mention}." activity_pubs: diff --git a/config/locales/es.yml b/config/locales/es.yml index 192b3298..7f71781c 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -105,8 +105,9 @@ es: text_approve: Aceptar text_reject: Rechazar text_report: Reportar + confirm_report: "¿Enviar el reporte a la instancia remota?" instances_btn_box: - text_check: Moderar caso por caso + text_pause: Moderar caso por caso text_allow: Permitir todo text_deny: Bloquear instancia profiles_btn_box: @@ -114,6 +115,7 @@ es: text_allow: Aprobar siempre text_block: Bloquear text_report: Reportar + confirm_report: "¿Enviar el reporte a la instancia remota?" remote_flags: report_message: "¡Hola! Une usuarie de Sutty CMS reportó esta cuenta en tu instancia. Todavía no tenemos soporte para mensajes personalizados. Podés contactarnos en %{panel_actor_mention}." activity_pubs: From 86ba17328db4823140766bdf0dc82d7b5f6f01ed Mon Sep 17 00:00:00 2001 From: f Date: Fri, 15 Mar 2024 14:01:00 -0300 Subject: [PATCH 068/133] feat: migas de pan para actores --- app/controllers/actor_moderations_controller.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/controllers/actor_moderations_controller.rb b/app/controllers/actor_moderations_controller.rb index bc4a059b..769be1fa 100644 --- a/app/controllers/actor_moderations_controller.rb +++ b/app/controllers/actor_moderations_controller.rb @@ -5,6 +5,11 @@ class ActorModerationsController < ApplicationController include ModerationConcern include ModerationFiltersConcern + before_action :authenticate_usuarie! + + breadcrumb -> { current_usuarie.email }, :edit_usuarie_registration_path + breadcrumb 'sites.index', :sites_path, match: :exact + ActorModeration.events.each do |actor_event| define_method(actor_event) do authorize actor_moderation @@ -26,9 +31,13 @@ class ActorModerationsController < ApplicationController # Ver el perfil remoto def show + breadcrumb I18n.t('moderation_queue.index.title'), site_moderation_queue_path(site) + @remote_profile = actor_moderation.actor.content @moderation_queue = rubanok_process(site.activity_pubs.where(actor_id: actor_moderation.actor_id), with: ActivityPubProcessor) + + breadcrumb @remote_profile['name'], '' end def action_on_several From 0fe043dd791dd30342f2f2197da4882e0aacfffb Mon Sep 17 00:00:00 2001 From: f Date: Fri, 15 Mar 2024 14:14:27 -0300 Subject: [PATCH 069/133] fix: informar errores de json closes #15593 --- app/jobs/activity_pub/fetch_job.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/jobs/activity_pub/fetch_job.rb b/app/jobs/activity_pub/fetch_job.rb index d7003c11..129908d3 100644 --- a/app/jobs/activity_pub/fetch_job.rb +++ b/app/jobs/activity_pub/fetch_job.rb @@ -31,6 +31,8 @@ class ActivityPub # Arreglar las relaciones con actividades también ActivityPub.where(object_id: object.id).update_all(object_type: object.type) unless current_type == object.type + rescue FastJsonparser::ParseError => e + ExceptionNotifier.notify_exception(e, data: { site: site.name, body: response.body }) end end end From 5db750675217ab5226a5955745e0736d805695eb Mon Sep 17 00:00:00 2001 From: f Date: Fri, 15 Mar 2024 14:26:12 -0300 Subject: [PATCH 070/133] =?UTF-8?q?fix:=20no=20se=20puede=20pausar=20todav?= =?UTF-8?q?=C3=ADa=20#15600?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/activity_pub.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/activity_pub.rb b/app/models/activity_pub.rb index bd6c816e..840cf3b1 100644 --- a/app/models/activity_pub.rb +++ b/app/models/activity_pub.rb @@ -8,8 +8,8 @@ # # @see {https://www.w3.org/TR/activitypub/#client-to-server-interactions} class ActivityPub < ApplicationRecord - IGNORED_EVENTS = %i[remove] - IGNORED_STATES = %i[removed] + IGNORED_EVENTS = %i[pause remove].freeze + IGNORED_STATES = %i[removed].freeze include AASM From 6412fc108efd2279582f88997f79fc19790f727d Mon Sep 17 00:00:00 2001 From: f Date: Fri, 15 Mar 2024 14:31:54 -0300 Subject: [PATCH 071/133] =?UTF-8?q?fix:=20no=20se=20puede=20rechazar=20lue?= =?UTF-8?q?go=20de=20aprobar=20todav=C3=ADa?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/activity_pub.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/activity_pub.rb b/app/models/activity_pub.rb index 840cf3b1..73006d1d 100644 --- a/app/models/activity_pub.rb +++ b/app/models/activity_pub.rb @@ -81,7 +81,7 @@ class ActivityPub < ApplicationRecord # La actividad fue rechazada event :reject do - transitions from: %i[paused approved], to: :rejected + transitions from: %i[paused], to: :rejected before do reject_remotely! From fcbff3e1c13a5b8500fd898e9bd2753ca070c853 Mon Sep 17 00:00:00 2001 From: f Date: Fri, 15 Mar 2024 16:52:42 -0300 Subject: [PATCH 072/133] fix: enviar el reporte firmado por el sitio #15605 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pasaban dos cosas: 1. para firmar correctamente, el cliente necesita recibir el path completo por su parámetro `endpoint` 2. la petición tiene que ser hecha por le misme actore que hace el reporte, como estábamos firmando con el sitio, mastodon creía que era un relay y esperaba que se envíen firmas ld --- .env | 2 ++ app/jobs/activity_pub/remote_flag_job.rb | 9 +++++++-- app/models/activity_pub/remote_flag.rb | 16 ++++++++++++---- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/.env b/.env index 480175f8..fe503b11 100644 --- a/.env +++ b/.env @@ -39,3 +39,5 @@ GITLAB_PROJECT= GITLAB_TOKEN= PGVER=15 PGPID=/run/postgresql.pid +PANEL_ACTOR_MENTION=@sutty@sutty.nl +PANEL_ACTOR_SITE_ID=1 diff --git a/app/jobs/activity_pub/remote_flag_job.rb b/app/jobs/activity_pub/remote_flag_job.rb index f5650d53..20833bd4 100644 --- a/app/jobs/activity_pub/remote_flag_job.rb +++ b/app/jobs/activity_pub/remote_flag_job.rb @@ -15,10 +15,15 @@ class ActivityPub def perform(remote_flag:) return if remote_flag.may_queue? + inbox = remote_flag.actor&.content&.[]('inbox') + + raise 'Inbox is missing for actor' if inbox.blank? + remote_flag.queue! - client = remote_flag.site.social_inbox.client_for(remote_flag.actor&.content['inbox']) - response = client.post(endpoint: '', body: remote_flag.content) + uri = URI.parse(inbox) + client = remote_flag.main_site.social_inbox.client_for(uri.origin) + response = client.post(endpoint: uri.path, body: remote_flag.content) raise 'No se pudo enviar el reporte' unless response.ok? diff --git a/app/models/activity_pub/remote_flag.rb b/app/models/activity_pub/remote_flag.rb index 70f09dcc..c3cc0fb0 100644 --- a/app/models/activity_pub/remote_flag.rb +++ b/app/models/activity_pub/remote_flag.rb @@ -2,8 +2,8 @@ class ActivityPub class RemoteFlag < ApplicationRecord - IGNORED_EVENTS = [] - IGNORED_STATES = [] + IGNORED_EVENTS = [].freeze + IGNORED_STATES = [].freeze include AASM @@ -42,10 +42,18 @@ class ActivityPub '@context' => 'https://www.w3.org/ns/activitystreams', 'id' => Rails.application.routes.url_helpers.v1_activity_pub_remote_flag_url(self, host: site.social_inbox_hostname), 'type' => 'Flag', - 'actor' => ENV.fetch('PANEL_ACTOR_ID') { "https://#{ENV['SUTTY']}/about.jsonld" }, + 'actor' => main_site.social_inbox.actor_id, 'content' => message.to_s, - 'object' => [ actor.uri ] + objects.pluck(:uri) + 'object' => [actor.uri] + objects.pluck(:uri) } end + + # Este es el sitio principal que actúa como origen del reporte. + # Tiene que tener la Social Inbox habilitada al mismo tiempo. + # + # @return [Site] + def main_site + @main_site ||= Site.find(ENV.fetch('PANEL_ACTOR_SITE_ID') { 1 }) + end end end From 364b63a075b141a01e429d30b0030e149d86d67f Mon Sep 17 00:00:00 2001 From: f Date: Fri, 15 Mar 2024 17:57:21 -0300 Subject: [PATCH 073/133] =?UTF-8?q?fix:=20notificar=20correctamente=20la?= =?UTF-8?q?=20excepci=C3=B3n=20#15608?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/activity_pub/activity/follow.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/activity_pub/activity/follow.rb b/app/models/activity_pub/activity/follow.rb index 393eb3b4..37dc9d26 100644 --- a/app/models/activity_pub/activity/follow.rb +++ b/app/models/activity_pub/activity/follow.rb @@ -13,7 +13,7 @@ class ActivityPub def update_activity_pub_state! activity_pub.approve! rescue Exception => e - ExceptionNotifier.notify_exception(e, { site: activity_pub.site.name, activity: self.id }) + ExceptionNotifier.notify_exception(e, data: { site: activity_pub.site.name, activity: self.id }) end end end From 00270b7829266120fa2e3beaa87ffde118cae38a Mon Sep 17 00:00:00 2001 From: f Date: Fri, 15 Mar 2024 18:05:05 -0300 Subject: [PATCH 074/133] fix: no mostrar likes #15596 --- app/models/activity_pub.rb | 16 ++++++++++++++-- app/processors/activity_pub_processor.rb | 9 ++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/app/models/activity_pub.rb b/app/models/activity_pub.rb index 73006d1d..3671000c 100644 --- a/app/models/activity_pub.rb +++ b/app/models/activity_pub.rb @@ -6,6 +6,12 @@ # una actividad, puede estar destinada a varies actores dentro de Sutty, # con lo que generamos una cola para cada une. # +# +# @todo Ya que une actore puede hacer varias actividades sobre el mismo +# objeto, lo correcto sería que la actividad a moderar sea una sola en +# lugar de una lista acumulativa. Es decir cada ActivityPub representa +# el estado del conjunto (Actor, Object, Activity) +# # @see {https://www.w3.org/TR/activitypub/#client-to-server-interactions} class ActivityPub < ApplicationRecord IGNORED_EVENTS = %i[pause remove].freeze @@ -105,10 +111,16 @@ class ActivityPub < ApplicationRecord # disponible, que según el scope por defecto, va a ser la primera de # la lista. def reject_remotely! - raise unless site.social_inbox.inbox.reject(id: activities.first.uri).ok? + fail! unless site.social_inbox.inbox.reject(id: activities.first.uri).ok? end def allow_remotely! - raise unless site.social_inbox.inbox.accept(id: activities.first.uri).ok? + with_failure_handling(activity: activities.first.uri) do + fail! unless site.social_inbox.inbox.accept(id: activities.first.uri).ok? + end + end + + def fail_message + activities.first&.uri || 'Activity missing' end end diff --git a/app/processors/activity_pub_processor.rb b/app/processors/activity_pub_processor.rb index 52cdb6d3..501b73a5 100644 --- a/app/processors/activity_pub_processor.rb +++ b/app/processors/activity_pub_processor.rb @@ -6,7 +6,14 @@ class ActivityPubProcessor < Rubanok::Processor # # Por ahora solo queremos moderar comentarios. prepare do - raw.where(object_type: %w[ActivityPub::Object::Note ActivityPub::Object::Article]).order(updated_at: :desc) + raw + .joins(:activities) + .where( + activity_pub_activities: { + type: %w[ActivityPub::Activity::Create ActivityPub::Activity::Update] + }, + object_type: %w[ActivityPub::Object::Note ActivityPub::Object::Article] + ).order(updated_at: :desc) end map :activity_pub_state, activate_always: true do |activity_pub_state: 'paused'| From 26e865661638e9af84acaf6dd0f331433603e89c Mon Sep 17 00:00:00 2001 From: f Date: Fri, 15 Mar 2024 18:08:34 -0300 Subject: [PATCH 075/133] fixup! fix: no mostrar likes #15596 --- app/models/activity_pub.rb | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/app/models/activity_pub.rb b/app/models/activity_pub.rb index 3671000c..cae054cd 100644 --- a/app/models/activity_pub.rb +++ b/app/models/activity_pub.rb @@ -111,16 +111,10 @@ class ActivityPub < ApplicationRecord # disponible, que según el scope por defecto, va a ser la primera de # la lista. def reject_remotely! - fail! unless site.social_inbox.inbox.reject(id: activities.first.uri).ok? + raise unless site.social_inbox.inbox.reject(id: activities.first.uri).ok? end def allow_remotely! - with_failure_handling(activity: activities.first.uri) do - fail! unless site.social_inbox.inbox.accept(id: activities.first.uri).ok? - end - end - - def fail_message - activities.first&.uri || 'Activity missing' + raise unless site.social_inbox.inbox.accept(id: activities.first.uri).ok? end end From 3a0f1584c1d292997bc850e8bbd7810810bfc6b6 Mon Sep 17 00:00:00 2001 From: f Date: Sat, 16 Mar 2024 11:32:16 -0300 Subject: [PATCH 076/133] fix: poder volver al sitio --- app/controllers/actor_moderations_controller.rb | 1 + app/controllers/moderation_queue_controller.rb | 1 + 2 files changed, 2 insertions(+) diff --git a/app/controllers/actor_moderations_controller.rb b/app/controllers/actor_moderations_controller.rb index 769be1fa..6316a4cf 100644 --- a/app/controllers/actor_moderations_controller.rb +++ b/app/controllers/actor_moderations_controller.rb @@ -31,6 +31,7 @@ class ActorModerationsController < ApplicationController # Ver el perfil remoto def show + breadcrumb site.title, site_posts_path(site) breadcrumb I18n.t('moderation_queue.index.title'), site_moderation_queue_path(site) @remote_profile = actor_moderation.actor.content diff --git a/app/controllers/moderation_queue_controller.rb b/app/controllers/moderation_queue_controller.rb index 3536dc95..4bd61e38 100644 --- a/app/controllers/moderation_queue_controller.rb +++ b/app/controllers/moderation_queue_controller.rb @@ -11,6 +11,7 @@ class ModerationQueueController < ApplicationController # Cola de moderación viendo todo el sitio def index + breadcrumb site.title, site_posts_path(site) breadcrumb I18n.t('moderation_queue.index.title'), '' # @todo cambiar el estado por query From adfd6c78bbb583ea0dc8936358152d771771fded Mon Sep 17 00:00:00 2001 From: f Date: Sat, 16 Mar 2024 14:15:32 -0300 Subject: [PATCH 077/133] feat: procesar actividades en segundo plano MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit en lugar de hacerlo en el momento, respondemos lo más rápido posible a la social inbox, ya que un webhook fallado genera un error en la social inbox, que genera un error en la instancia remota, que va a volver a intentar muchas veces. ahora recibimos una vez, si falla el procesamiento, lo detenemos para que alguien humane actúe al respecto. --- .../v1/webhooks/social_inbox_controller.rb | 171 ++---------------- app/jobs/activity_pub/process_job.rb | 154 ++++++++++++++++ 2 files changed, 167 insertions(+), 158 deletions(-) create mode 100644 app/jobs/activity_pub/process_job.rb diff --git a/app/controllers/api/v1/webhooks/social_inbox_controller.rb b/app/controllers/api/v1/webhooks/social_inbox_controller.rb index 7b17c47b..6ac91a51 100644 --- a/app/controllers/api/v1/webhooks/social_inbox_controller.rb +++ b/app/controllers/api/v1/webhooks/social_inbox_controller.rb @@ -5,196 +5,51 @@ module Api module Webhooks # Recibe webhooks de la Social Inbox # - # @todo Mover todo a un Job que obtenga el objeto remoto antes de - # instanciar el objeto localmente en lugar de arreglarlo después y - # poder responder lo más rápido posible el webhook. # @see {https://www.w3.org/TR/activitypub/} class SocialInboxController < BaseController include Api::V1::Webhooks::Concerns::WebhookConcern + # Validar que el token sea correcto + before_action :usuarie + # Cuando una actividad ingresa en la cola de moderación, la # recibimos por acá # - # Vamos a recibir Create, Update, Delete, Follow, Undo y obtener - # el objeto dentro de cada una para guardar un estado asociado - # al sitio. + # Vamos a recibir Create, Update, Delete, Follow, Undo, + # Announce, Like y obtener el objeto dentro de cada una para + # guardar un estado asociado al sitio. # # El objeto del estado puede ser un objeto o une actore, # dependiendo de la actividad. def moderationqueued - # Devuelve un error si el token no es válido - usuarie.present? + process! :paused - ::ActivityPub.transaction do - - # Crea todos los registros necesarios y actualiza el estado - actor.present? - instance.present? - object.present? - activity_pub.present? - - activity.update_activity_pub_state! - end - rescue ActiveRecord::RecordInvalid => e - ExceptionNotifier.notify_exception(e, - data: { site: site.name, usuarie: usuarie.email, - activity: original_activity }) - ensure head :accepted end # Cuando la Social Inbox acepta una actividad, la recibimos # igual y la guardamos por si cambiamos de idea. - # - # @todo DRY def onapproved - ::ActivityPub.transaction do - actor.present? - instance.present? - object.present? - activity.present? - activity_pub.update(aasm_state: 'approved') - activity.update_activity_pub_state! - end + process! :approved head :accepted end # Cuando la Social Inbox rechaza una actividad, la recibimos # igual y la guardamos por si cambiamos de idea. - # - # @todo DRY def onrejected - ::ActivityPub.transaction do - actor.present? - instance.present? - object.present? - activity.present? - activity_pub.update(aasm_state: 'rejected') - end + process! :rejected head :accepted end private - # Si el objeto ya viene incorporado en la actividad o lo tenemos - # que traer remotamente. + # Envía la actividad para procesamiento por separado. # - # @return [Bool] - def object_embedded? - @object_embedded ||= original_activity[:object].is_a?(Hash) - end - - # Encuentra la URI del objeto o falla si no la encuentra. - # - # @return [String] - def object_uri - @object_uri ||= ::ActivityPub.uri_from_object(original_activity[:object]) - ensure - raise ActiveRecord::RecordNotFound, 'object id missing' if @object_uri.blank? - end - - # Atajo a la instancia - # - # @return [ActivityPub::Instance] - def instance - actor.instance - end - - # Genera un objeto a partir de la actividad. Si el objeto ya - # existe, actualiza su contenido. Si el objeto no viene - # incorporado, obtenemos el contenido más tarde. - # - # @return [ActivityPub::Object] - def object - @object ||= ::ActivityPub::Object.find_or_initialize_by(uri: object_uri).tap do |o| - # XXX: Si el objeto es una actividad, esto siempre va a ser - # Generic - o.type ||= 'ActivityPub::Object::Generic' - - if object_embedded? - o.content = original_object - begin - type = original_object[:type].presence - o.type = "ActivityPub::Object::#{type}".constantize if type - rescue NameError - end - end - - o.save! - - # XXX: el objeto necesita ser guardado antes de poder - # procesarlo. No usamos GlobalID porque el tipo de objeto - # cambia y produce un error de deserialización. - ::ActivityPub::FetchJob.perform_later(site: site, object_id: o.id) unless object_embedded? - end - end - - # Genera el seguimiento del estado del objeto con respecto al - # sitio. - # - # @return [ActivityPub] - def activity_pub - @activity_pub ||= site.activity_pubs.find_or_create_by!(site: site, actor: actor, instance: instance, object_id: object.id, object_type: object.type) - end - - # Crea la actividad y la vincula con el estado - # - # @return [ActivityPub::Activity] - def activity - @activity ||= - ::ActivityPub::Activity - .type_from(original_activity) - .find_or_initialize_by(uri: original_activity[:id], activity_pub: activity_pub, actor: actor).tap do |a| - a.content = original_activity.dup - a.content[:object] = object.uri - a.save! - end - end - - # Actor, si no hay instancia, la crea en el momento, junto con - # su estado de moderación. - # - # @return [Actor] - def actor - @actor ||= ::ActivityPub::Actor.find_or_initialize_by(uri: original_activity[:actor]).tap do |a| - unless a.instance - a.instance = ::ActivityPub::Instance.find_or_create_by(hostname: URI.parse(a.uri).hostname) - - ::ActivityPub::InstanceFetchJob.perform_later(site: site, instance: a.instance) - end - - site.instance_moderations.find_or_create_by(instance: a.instance) - - a.save! - - site.actor_moderations.find_or_create_by(actor: a) - - ::ActivityPub::ActorFetchJob.perform_later(site: site, actor: a) - end - end - - # Descubre la actividad recibida, generando un error si la - # actividad no está dirigida a nosotres. - # - # @todo Validar formato - # @return [Hash] - def original_activity - @original_activity ||= FastJsonparser.parse(request.raw_post).tap do |activity| - raise '@context missing' unless activity[:@context].presence - raise 'id missing' unless activity[:id].presence - raise 'object missing' unless activity[:object].presence - rescue RuntimeError => e - raise ActiveRecord::RecordNotFound, e.message - end - end - - # @return [Hash,String] - def original_object - @original_object ||= original_activity[:object].dup.tap do |o| - o[:@context] = original_activity[:@context].dup - end + # @param initial_state [Symbol] + def process!(initial_state) + ::ActivityPub::ProcessJob.perform_later(site: site, body: request.raw_post, initial_state: initial_state) end end end diff --git a/app/jobs/activity_pub/process_job.rb b/app/jobs/activity_pub/process_job.rb new file mode 100644 index 00000000..6554b44d --- /dev/null +++ b/app/jobs/activity_pub/process_job.rb @@ -0,0 +1,154 @@ +# frozen_string_literal: true + +class ActivityPub + # Procesar las actividades a medida que llegan + class ProcessJob < ApplicationJob + attr_reader :body + + # Procesa la actividad en segundo plano + # + # @param :body [String] + # @param :initial_state [Symbol,String] + def perform(site:, body:, initial_state: :paused) + @body = body + @site = site + + ActiveRecord::Base.connection_pool.with_connection do + ::ActivityPub.transaction do + # Crea todos los registros necesarios y actualiza el estado + actor.present? + instance.present? + object.present? + activity_pub.present? + activity_pub.update(aasm_state: initial_state) + + activity.update_activity_pub_state! + end + end + # Al generar una excepción, en lugar de seguir intentando, enviamos + # el reporte. + rescue Exception => e + ExceptionNotifier.notify_exception(e, data: { site: site.name, activity: original_activity }) + end + + private + + # Si el objeto ya viene incorporado en la actividad o lo tenemos + # que traer remotamente. + # + # @return [Bool] + def object_embedded? + @object_embedded ||= original_activity[:object].is_a?(Hash) + end + + # Encuentra la URI del objeto o falla si no la encuentra. + # + # @return [String] + def object_uri + @object_uri ||= ::ActivityPub.uri_from_object(original_activity[:object]) + ensure + raise ActiveRecord::RecordNotFound, 'object id missing' if @object_uri.blank? + end + + # Atajo a la instancia + # + # @return [ActivityPub::Instance] + def instance + actor.instance + end + + # Genera un objeto a partir de la actividad. Si el objeto ya + # existe, actualiza su contenido. Si el objeto no viene + # incorporado, obtenemos el contenido más tarde. + # + # @return [ActivityPub::Object] + def object + @object ||= ::ActivityPub::Object.find_or_initialize_by(uri: object_uri).tap do |o| + # XXX: Si el objeto es una actividad, esto siempre va a ser + # Generic + o.type ||= 'ActivityPub::Object::Generic' + + if object_embedded? + o.content = original_object + begin + type = original_object[:type].presence + o.type = "ActivityPub::Object::#{type}".constantize if type + rescue NameError + end + end + + o.save! + + # XXX: el objeto necesita ser guardado antes de poder + # procesarlo. No usamos GlobalID porque el tipo de objeto + # cambia y produce un error de deserialización. + ::ActivityPub::FetchJob.perform_later(site: site, object_id: o.id) unless object_embedded? + end + end + + # Genera el seguimiento del estado del objeto con respecto al + # sitio. + # + # @return [ActivityPub] + def activity_pub + @activity_pub ||= site.activity_pubs.find_or_create_by!(site: site, actor: actor, instance: instance, + object_id: object.id, object_type: object.type) + end + + # Crea la actividad y la vincula con el estado + # + # @return [ActivityPub::Activity] + def activity + @activity ||= + ::ActivityPub::Activity + .type_from(original_activity) + .find_or_initialize_by(uri: original_activity[:id], activity_pub: activity_pub, actor: actor).tap do |a| + a.content = original_activity.dup + a.content[:object] = object.uri + a.save! + end + end + + # Actor, si no hay instancia, la crea en el momento, junto con + # su estado de moderación. + # + # @return [Actor] + def actor + @actor ||= ::ActivityPub::Actor.find_or_initialize_by(uri: original_activity[:actor]).tap do |a| + unless a.instance + a.instance = ::ActivityPub::Instance.find_or_create_by(hostname: URI.parse(a.uri).hostname) + + ::ActivityPub::InstanceFetchJob.perform_later(site: site, instance: a.instance) + end + + site.instance_moderations.find_or_create_by(instance: a.instance) + + a.save! + + site.actor_moderations.find_or_create_by(actor: a) + + ::ActivityPub::ActorFetchJob.perform_later(site: site, actor: a) + end + end + + # @return [Hash,String] + def original_object + @original_object ||= original_activity[:object].dup.tap do |o| + o[:@context] = original_activity[:@context].dup + end + end + + # Descubre la actividad recibida, generando un error si la + # actividad no está dirigida a nosotres. + # + # @todo Validar formato con Dry::Schema + # @return [Hash] + def original_activity + @original_activity ||= FastJsonparser.parse(body).tap do |activity| + raise '@context missing' unless activity[:@context].present? + raise 'id missing' unless activity[:id].present? + raise 'object missing' unless activity[:object].present? + end + end + end +end From 1dae918a179ecca3f18edfe286e16d58b7546398 Mon Sep 17 00:00:00 2001 From: f Date: Sat, 16 Mar 2024 14:17:28 -0300 Subject: [PATCH 078/133] feat: poder gestionar la cola de tareas --- Gemfile | 1 + Gemfile.lock | 18 +++++++++++++++++- config/initializers/que_web.rb | 5 +++++ config/routes.rb | 3 +++ 4 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 config/initializers/que_web.rb diff --git a/Gemfile b/Gemfile index e314c172..e5e6ed10 100644 --- a/Gemfile +++ b/Gemfile @@ -83,6 +83,7 @@ gem 'rubanok' gem 'after_commit_everywhere', '~> 1.0' gem 'aasm' +gem 'que-web' # database gem 'hairtrigger' diff --git a/Gemfile.lock b/Gemfile.lock index 751fc73e..7087e013 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -97,6 +97,7 @@ GEM ast (2.4.2) autoprefixer-rails (10.4.13.0) execjs (~> 2) + base64 (0.2.0) bcrypt (3.1.20-x86_64-linux-musl) bcrypt_pbkdf (1.1.0-x86_64-linux-musl) benchmark-ips (2.12.0) @@ -370,6 +371,8 @@ GEM i18n (>= 0.6.10, < 2) request_store (~> 1.0) multi_xml (0.6.0) + mustermann (3.0.0) + ruby2_keywords (~> 0.0.1) net-imap (0.4.9) date net-protocol @@ -409,12 +412,18 @@ GEM pundit (2.3.1) activesupport (>= 3.0.0) que (2.2.1) + que-web (0.10.0) + que (>= 1) + sinatra racc (1.7.3-x86_64-linux-musl) rack (2.2.8) rack-cors (2.0.1) rack (>= 2.0.0) rack-mini-profiler (3.1.0) rack (>= 1.2.0) + rack-protection (3.2.0) + base64 (>= 0.1.0) + rack (~> 2.2, >= 2.2.4) rack-proxy (0.7.7) rack rack-test (2.1.0) @@ -513,6 +522,7 @@ GEM ruby-statistics (3.0.2) ruby-vips (2.2.0) ffi (~> 1.12) + ruby2_keywords (0.0.5) ruby2ruby (2.5.0) ruby_parser (~> 3.1) sexp_processor (~> 4.6) @@ -539,6 +549,11 @@ GEM sexp_processor (4.17.0) simpleidn (0.2.1) unf (~> 0.1.4) + sinatra (3.2.0) + mustermann (~> 3.0) + rack (~> 2.2, >= 2.2.4) + rack-protection (= 3.2.0) + tilt (~> 2.0) sourcemap (0.1.1) spring (4.1.1) spring-watcher-listen (2.1.0) @@ -626,7 +641,7 @@ DEPENDENCIES devise devise-i18n devise_invitable - distributed-press-api-client (~> 0.4.0) + distributed-press-api-client (~> 0.4.1) dotenv-rails down ed25519 @@ -670,6 +685,7 @@ DEPENDENCIES puma pundit que + que-web rack-cors rack-mini-profiler rails (~> 6.1.0) diff --git a/config/initializers/que_web.rb b/config/initializers/que_web.rb new file mode 100644 index 00000000..192256db --- /dev/null +++ b/config/initializers/que_web.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +Que::Web.use(Rack::Auth::Basic) do |user, password| + [user, password] == [ENV['HTTP_BASIC_USER'], ENV['HTTP_BASIC_PASSWORD']] +end diff --git a/config/routes.rb b/config/routes.rb index 054b7f4d..4d43d66a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -4,6 +4,9 @@ Rails.application.routes.draw do devise_for :usuaries get '/.well-known/change-password', to: redirect('/usuaries/edit') + require 'que/web' + mount Que::Web => '/que' + root 'application#index' constraints(Constraints::ApiSubdomain.new) do From 8f91b748ff6ed9536d13093efd72e228bb66de3f Mon Sep 17 00:00:00 2001 From: f Date: Sat, 16 Mar 2024 14:27:28 -0300 Subject: [PATCH 079/133] =?UTF-8?q?fix:=20mostrar=20botones=20de=20acci?= =?UTF-8?q?=C3=B3n=20en=20masa?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/views/components/_btn_base.haml | 4 ++-- app/views/components/_dropdown_button.haml | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/views/components/_btn_base.haml b/app/views/components/_btn_base.haml index f9227482..faa5c85f 100644 --- a/app/views/components/_btn_base.haml +++ b/app/views/components/_btn_base.haml @@ -3,7 +3,7 @@ - local_assigns[:method] ||= 'patch' - local_assigns[:class] ||= 'btn-secondary' - local_assigns[:class] = "btn #{local_assigns[:class]}" +- local_assigns.delete(:text) --# @todo path es obligatorio -= button_to local_assigns[:path], **local_assigns.compact do += button_to(path, **local_assigns.compact) do = text diff --git a/app/views/components/_dropdown_button.haml b/app/views/components/_dropdown_button.haml index c0f12754..d6de6c8e 100644 --- a/app/views/components/_dropdown_button.haml +++ b/app/views/components/_dropdown_button.haml @@ -1,4 +1,6 @@ -# @param name [String] @param value [String] -%button.dropdown-item{type: 'submit', data: { target: 'dropdown.item' }, name: name, value: value, **local_assigns.compact } + @param text [String] +- local_assigns.delete(:text) +%button.dropdown-item{type: 'submit', data: { target: 'dropdown.item' }, name: name, value: value, **local_assigns.compact }= text From 8ff5626e9968407d5b12f5e9a77fd3cb739ff77d Mon Sep 17 00:00:00 2001 From: f Date: Sat, 16 Mar 2024 15:12:55 -0300 Subject: [PATCH 080/133] feat: no permitir entrar al evento si falla la request --- app/controllers/activity_pubs_controller.rb | 14 +++--- .../actor_moderations_controller.rb | 13 ++--- .../instance_moderations_controller.rb | 13 ++--- app/models/activity_pub.rb | 21 ++++---- app/models/actor_moderation.rb | 32 +++++-------- app/models/concerns/aasm_events_concern.rb | 29 ++++------- app/models/fediblock_state.rb | 48 ++++++++++--------- app/models/instance_moderation.rb | 28 ++++------- 8 files changed, 88 insertions(+), 110 deletions(-) diff --git a/app/controllers/activity_pubs_controller.rb b/app/controllers/activity_pubs_controller.rb index edece8f8..225311c2 100644 --- a/app/controllers/activity_pubs_controller.rb +++ b/app/controllers/activity_pubs_controller.rb @@ -9,14 +9,16 @@ class ActivityPubsController < ApplicationController authorize activity_pub activity_pub.update(remote_flag_params(activity_pub)) if event == :report - activity_pub.public_send(:"#{event}!") if activity_pub.public_send(:"may_#{event}?") - flash[:success] = I18n.t("activity_pubs.#{event}.success") - rescue Exception => e - ExceptionNotifier.notify_exception(e, data: { site: site.name, params: params.permit!.to_h }) + message = + if activity_pub.public_send(:"may_#{event}?") && activity_pub.public_send(:"#{event}!") + :success + else + :error + end + + flash[message] = I18n.t("activity_pubs.#{event}.#{message}") - flash[:error] = I18n.t("activity_pubs.#{event}.error") - ensure redirect_to_moderation_queue! end end diff --git a/app/controllers/actor_moderations_controller.rb b/app/controllers/actor_moderations_controller.rb index 6316a4cf..cb7a63a9 100644 --- a/app/controllers/actor_moderations_controller.rb +++ b/app/controllers/actor_moderations_controller.rb @@ -17,14 +17,15 @@ class ActorModerationsController < ApplicationController # Crea una RemoteFlag si se envían los parámetros adecuados actor_moderation.update(remote_flag_params(actor_moderation)) if actor_event == :report - actor_moderation.public_send(:"#{actor_event}!") if actor_moderation.public_send(:"may_#{actor_event}?") + message = + if actor_moderation.public_send(:"may_#{actor_event}?") && actor_moderation.public_send(:"#{actor_event}!") + :success + else + :error + end - flash[:success] = I18n.t("actor_moderations.#{actor_event}.success") - rescue Exception => e - ExceptionNotifier.notify_exception(e, data: { site: site.name, params: params.permit!.to_h }) + flash[message] = I18n.t("actor_moderations.#{actor_event}.#{message}") - flash[:error] = I18n.t("actor_moderations.#{actor_event}.error") - ensure redirect_to_moderation_queue! end end diff --git a/app/controllers/instance_moderations_controller.rb b/app/controllers/instance_moderations_controller.rb index 06f5cfc1..13d7f428 100644 --- a/app/controllers/instance_moderations_controller.rb +++ b/app/controllers/instance_moderations_controller.rb @@ -8,14 +8,15 @@ class InstanceModerationsController < ApplicationController define_method(event) do authorize instance_moderation - instance_moderation.public_send(:"#{event}!") if instance_moderation.public_send(:"may_#{event}?") + message = + if instance_moderation.public_send(:"may_#{event}?") && instance_moderation.public_send(:"#{event}!") + :success + else + :error + end - flash[:success] = I18n.t("instance_moderations.#{event}.success") - rescue Exception => e - ExceptionNotifier.notify_exception(e, data: { site: site.name, params: params.permit!.to_h }) + flash[message] = I18n.t("instance_moderations.#{event}.#{message}") - flash[:error] = I18n.t("instance_moderations.#{event}.error") - ensure redirect_to_moderation_queue! end end diff --git a/app/models/activity_pub.rb b/app/models/activity_pub.rb index cae054cd..8142ecfb 100644 --- a/app/models/activity_pub.rb +++ b/app/models/activity_pub.rb @@ -47,14 +47,19 @@ class ActivityPub < ApplicationRecord # Todavía no hay una decisión sobre el objeto state :paused, initial: true # Le usuarie aprobó el objeto - state :approved + state :approved, before_enter: :allow_remotely! # Le usuarie rechazó el objeto - state :rejected + state :rejected, before_enter: :reject_remotely! # Le usuarie reportó el objeto state :reported # Le actore eliminó el objeto state :removed + # Gestionar todos los errores + error_on_all_events do |e| + ExceptionNotifier.notify_exception(e, data: { site: site.name, activity_pub: self.id, activity: activities.first.uri }) + end + # Se puede volver a pausa en caso de actualización remota, para # revisar los cambios. event :pause do @@ -67,7 +72,7 @@ class ActivityPub < ApplicationRecord event :remove do transitions to: :removed - before do + after do next if object.blank? object.update(content: {}) unless object.content.empty? @@ -79,26 +84,18 @@ class ActivityPub < ApplicationRecord # webhook a modo de confirmación. event :approve do transitions from: %i[paused], to: :approved - - before do - allow_remotely! - end end # La actividad fue rechazada event :reject do transitions from: %i[paused], to: :rejected - - before do - reject_remotely! - end end # Solo podemos reportarla luego de rechazarla event :report do transitions from: :rejected, to: :reported - before do + after do ActivityPub::RemoteFlagJob.perform_later(remote_flag: remote_flag) if remote_flag.waiting? end end diff --git a/app/models/actor_moderation.rb b/app/models/actor_moderation.rb index 3b183a4f..c566b844 100644 --- a/app/models/actor_moderation.rb +++ b/app/models/actor_moderation.rb @@ -2,8 +2,8 @@ # Mantiene la relación entre Site y Actor class ActorModeration < ApplicationRecord - IGNORED_EVENTS = %i[remove] - IGNORED_STATES = %i[removed] + IGNORED_EVENTS = %i[remove].freeze + IGNORED_STATES = %i[removed].freeze include AASM @@ -14,38 +14,30 @@ class ActorModeration < ApplicationRecord accepts_nested_attributes_for :remote_flag aasm do - state :paused, initial: true - state :allowed - state :blocked + state :paused, initial: true, before_enter: :pause_remotely! + state :allowed, before_enter: :allow_remotely! + state :blocked, before_enter: :block_remotely! state :reported state :removed + error_on_all_events do |e| + ExceptionNotifier.notify_exception(e, data: { site: site.name, actor: actor.uri, actor_moderation: id }) + end + event :pause do transitions from: %i[allowed blocked reported], to: :paused - - before do - pause_remotely! - end end # Al permitir una cuenta no se permiten todos los comentarios # pendientes de moderación que ya hizo. event :allow do transitions from: %i[paused blocked reported], to: :allowed - - before do - allow_remotely! - end end # Al bloquear una cuenta no se bloquean todos los comentarios # pendientes de moderación que hizo. event :block do transitions from: %i[paused allowed], to: :blocked - - before do - block_remotely! - end end # Al reportar, necesitamos asociar una RemoteFlag para poder @@ -53,7 +45,7 @@ class ActorModeration < ApplicationRecord event :report do transitions from: %i[blocked], to: :reported - before do + after do ActivityPub::RemoteFlagJob.perform_later(remote_flag: remote_flag) if remote_flag.waiting? end end @@ -63,8 +55,8 @@ class ActorModeration < ApplicationRecord event :remove do transitions to: :removed - before do - site.activity_pubs.where(actor_id: self.actor_id).remove_all! + after do + site.activity_pubs.where(actor_id: actor_id).remove_all! end end end diff --git a/app/models/concerns/aasm_events_concern.rb b/app/models/concerns/aasm_events_concern.rb index 4de5f748..788e9e1a 100644 --- a/app/models/concerns/aasm_events_concern.rb +++ b/app/models/concerns/aasm_events_concern.rb @@ -16,7 +16,7 @@ module AasmEventsConcern # # @return [Array] def self.transitionable_events(current_state) - self.events.select do |event| + events.select do |event| aasm.events.find { |x| x.name == event }.transitions_from_state? current_state end end @@ -32,19 +32,15 @@ module AasmEventsConcern # scope actual. # # @return [Bool] Si hubo al menos un error, devuelve false. - self.aasm.events.map(&:name).each do |event| + aasm.events.map(&:name).each do |event| define_singleton_method(:"#{event}_all!") do - success = true + successes = [] - self.find_each do |object| - object.public_send(:"#{event}!") if object.public_send(:"may_#{event}?") - rescue Exception => e - success = false - - notify_exception! e, object + find_each do |object| + successes << (object.public_send(:"may_#{event}?") && object.public_send(:"#{event}!")) end - success + successes.all? end # Ejecuta la transición del evento en la base de datos sin @@ -53,20 +49,13 @@ module AasmEventsConcern # # @return [Integer] Registros modificados define_singleton_method(:"#{event}_all_without_callbacks!") do - aasm_event = self.aasm.events.find { |e| e.name == event } + aasm_event = aasm.events.find { |e| e.name == event } to_state = aasm_event.transitions.map(&:to).first from_states = aasm_event.transitions.map(&:from) - self.unscope(where: :aasm_state).where(aasm_state: from_states).update_all(aasm_state: to_state, updated_at: Time.now) + unscope(where: :aasm_state).where(aasm_state: from_states).update_all(aasm_state: to_state, + updated_at: Time.now) end end - - # Envía notificación de errores - # - # @param exception [Exception] - # @param record [ApplicationRecord] - def self.notify_exception!(exception, record) - ExceptionNotifier.notify_exception(exception, data: { record_type: record.class.name, record_id: record.id }) - end end end diff --git a/app/models/fediblock_state.rb b/app/models/fediblock_state.rb index 9dde1db3..a6faa14f 100644 --- a/app/models/fediblock_state.rb +++ b/app/models/fediblock_state.rb @@ -20,20 +20,15 @@ class FediblockState < ApplicationRecord # Aunque queramos las listas habilitadas por defecto, tenemos que # habilitarlas luego de crearlas para poder generar la lista de # bloqueo en la Social Inbox. - state :disabled, initial: true - state :enabled + state :disabled, initial: true, before_enter: :disable_remotely_and_pause_instances! + state :enabled, before_enter: :enable_remotely_and_block_instances! + + error_on_all_events do |e| + ExceptionNotifier.notify_exception(e, data: { site: site.name, fediblock: id }) + end event :enable do transitions from: :disabled, to: :enabled - - before do - # Bloquear todos las instancias de este Fediblock - enable_remotely! list_names(fediblock.hostnames) - - # Luego esta tarea crea las que falten e ignora las que ya se - # bloquearon. - ActivityPub::InstanceModerationJob.perform_now(site: site, hostnames: fediblock.hostnames, perform_remotely: false) - end end # Al deshabilitar, las listas pasan a modo pausa, a menos que estén @@ -44,22 +39,31 @@ class FediblockState < ApplicationRecord # de list_names event :disable do transitions from: :enabled, to: :disabled - - before do - # Deshabilitar todas las instancias que no estén habilitadas por - # otros fediblocks - disable_remotely! list_names(unique_hostnames) - - # Pausar todas las moderaciones de las instancias que no estén - # bloqueadas por otros fediblocks. - instance_ids = ActivityPub::Instance.where(hostname: unique_hostnames).ids - site.instance_moderations.where(instance_id: instance_ids).pause_all_without_callbacks! - end end end private + def enable_remotely_and_block_instances! + # Bloquear todos las instancias de este Fediblock + enable_remotely! list_names(fediblock.hostnames) + + # Luego esta tarea crea las que falten e ignora las que ya se + # bloquearon. + ActivityPub::InstanceModerationJob.perform_now(site: site, hostnames: fediblock.hostnames, perform_remotely: false) + end + + def disable_remotely_and_pause_instances! + # Deshabilitar todas las instancias que no estén habilitadas por + # otros fediblocks + disable_remotely! list_names(unique_hostnames) + + # Pausar todas las moderaciones de las instancias que no estén + # bloqueadas por otros fediblocks. + instance_ids = ActivityPub::Instance.where(hostname: unique_hostnames).ids + site.instance_moderations.where(instance_id: instance_ids).pause_all_without_callbacks! + end + # Devuelve los hostnames únicos a esta instancia. # # @return [Array] diff --git a/app/models/instance_moderation.rb b/app/models/instance_moderation.rb index 9cb6ffdc..7c76262e 100644 --- a/app/models/instance_moderation.rb +++ b/app/models/instance_moderation.rb @@ -2,8 +2,8 @@ # Mantiene el registro de relaciones entre sitios e instancias class InstanceModeration < ApplicationRecord - IGNORED_EVENTS = [] - IGNORED_STATES = [] + IGNORED_EVENTS = [].freeze + IGNORED_STATES = [].freeze include AASM @@ -11,38 +11,30 @@ class InstanceModeration < ApplicationRecord belongs_to :instance, class_name: 'ActivityPub::Instance' aasm do - state :paused, initial: true - state :allowed - state :blocked + state :paused, initial: true, before_enter: :pause_remotely! + state :allowed, before_enter: :allow_remotely! + state :blocked, before_enter: :block_remotely! + + error_on_all_events do |e| + ExceptionNotifier.notify_exception(e, data: { site: site.name, instance: instance.hostname, instance_moderation: id }) + end # Al volver la instancia a pausa no cambiamos el estado de # moderación de actores pre-existente. event :pause do transitions from: %i[allowed blocked], to: :paused - - before do - pause_remotely! - end end # Al permitir, solo bloqueamos la instancia, sin modificar el estado # de les actores y comentarios retroactivamente. event :allow do transitions from: %i[paused blocked], to: :allowed - - before do - allow_remotely! - end end # Al bloquear, solo bloqueamos la instancia, sin modificar el estado # de les actores y comentarios retroactivamente. event :block do transitions from: %i[paused allowed], to: :blocked - - before do - block_remotely! - end end end @@ -51,7 +43,7 @@ class InstanceModeration < ApplicationRecord # @return [Array] def actor_ids - ActivityPub::Actor.where(instance_id: self.instance_id).ids + ActivityPub::Actor.where(instance_id: instance_id).ids end # Elimina la instancia de todas las listas From f6b2895b58a0f9bf2cd8cc19e52f112afe01e9d4 Mon Sep 17 00:00:00 2001 From: f Date: Sat, 16 Mar 2024 16:36:00 -0300 Subject: [PATCH 081/133] =?UTF-8?q?fix:=20aprobar=20las=20peticiones=20de?= =?UTF-8?q?=20seguimiento=20autom=C3=A1ticamente?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/activity_pub/activity/follow.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/models/activity_pub/activity/follow.rb b/app/models/activity_pub/activity/follow.rb index 37dc9d26..b4c34d7a 100644 --- a/app/models/activity_pub/activity/follow.rb +++ b/app/models/activity_pub/activity/follow.rb @@ -11,9 +11,7 @@ class ActivityPub class Follow < ActivityPub::Activity # Auto-aprobar la solicitud de seguimiento def update_activity_pub_state! - activity_pub.approve! - rescue Exception => e - ExceptionNotifier.notify_exception(e, data: { site: activity_pub.site.name, activity: self.id }) + activity_pub.approve! if activity_pub.may_approve? end end end From b972e53e4821c563eaebabb5e1cbcb899ea063f8 Mon Sep 17 00:00:00 2001 From: f Date: Sat, 16 Mar 2024 17:53:29 -0300 Subject: [PATCH 082/133] =?UTF-8?q?feat:=20guardar=20la=20menci=C3=B3n=20d?= =?UTF-8?q?e=20le=20autore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/jobs/activity_pub/fetch_job.rb | 7 +++++- app/models/activity_pub/actor.rb | 9 ++++--- .../20240316203721_add_mention_to_actors.rb | 24 +++++++++++++++++++ 3 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 db/migrate/20240316203721_add_mention_to_actors.rb diff --git a/app/jobs/activity_pub/fetch_job.rb b/app/jobs/activity_pub/fetch_job.rb index 129908d3..e9220bfc 100644 --- a/app/jobs/activity_pub/fetch_job.rb +++ b/app/jobs/activity_pub/fetch_job.rb @@ -29,8 +29,13 @@ class ActivityPub object.update(content: content, type: ActivityPub::Object.type_from(content).name) + return if current_type == object.type + + object = ::ActivityPub::Object.find(object_id) + object.actor.save if object.actor_type? + # Arreglar las relaciones con actividades también - ActivityPub.where(object_id: object.id).update_all(object_type: object.type) unless current_type == object.type + ActivityPub.where(object_id: object.id).update_all(object_type: object.type) rescue FastJsonparser::ParseError => e ExceptionNotifier.notify_exception(e, data: { site: site.name, body: response.body }) end diff --git a/app/models/activity_pub/actor.rb b/app/models/activity_pub/actor.rb index b03145e7..a8cc0d5d 100644 --- a/app/models/activity_pub/actor.rb +++ b/app/models/activity_pub/actor.rb @@ -18,19 +18,22 @@ class ActivityPub # Les actores son únicxs a toda la base de datos validates :uri, presence: true, url: true, uniqueness: true + before_save :mentionize! + # Obtiene el nombre de la Actor como mención, solo si obtuvimos el # contenido de antemano. # # @return [String, nil] - def mention + def mentionize! + return if mention.present? return if content['preferredUsername'].blank? return if instance.blank? - @mention ||= "@#{content['preferredUsername']}@#{instance.hostname}" + self.mention ||= "@#{content['preferredUsername']}@#{instance.hostname}" end def object - @object ||= ActivityPub::Object::Person.find_or_initialize_by(uri: uri) + @object ||= ActivityPub::Object.find_or_initialize_by(uri: uri) end def content diff --git a/db/migrate/20240316203721_add_mention_to_actors.rb b/db/migrate/20240316203721_add_mention_to_actors.rb new file mode 100644 index 00000000..caa4f526 --- /dev/null +++ b/db/migrate/20240316203721_add_mention_to_actors.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +# Guarda la mención en la tabla de actores +class AddMentionToActors < ActiveRecord::Migration[6.1] + def up + add_column :activity_pub_actors, :mention, :string, null: true + + actor_types = %w[ + ActivityPub::Object::Application + ActivityPub::Object::Group + ActivityPub::Object::Organization + ActivityPub::Object::Person + ActivityPub::Object::Service + ] + + ActivityPub::Object.where(type: actor_types).where.not(content: {}).find_each do |object| + ActivityPub::Actor.find_by_uri(object.uri)&.save + end + end + + def down + remove_column :activity_pub_actors, :mention + end +end From 769eae61574e859c0bfc6078785f7fb28ef6aadf Mon Sep 17 00:00:00 2001 From: f Date: Sat, 16 Mar 2024 19:05:12 -0300 Subject: [PATCH 083/133] feat: sincronizar en segundo plano --- .../actor_moderations_controller.rb | 2 +- .../activity_pub/instance_moderation_job.rb | 2 + app/jobs/activity_pub/sync_lists_job.rb | 70 +++++++++++++++++++ app/models/actor_moderation.rb | 33 +++------ app/models/fediblock_state.rb | 55 ++++----------- app/models/instance_moderation.rb | 42 ++--------- 6 files changed, 100 insertions(+), 104 deletions(-) create mode 100644 app/jobs/activity_pub/sync_lists_job.rb diff --git a/app/controllers/actor_moderations_controller.rb b/app/controllers/actor_moderations_controller.rb index cb7a63a9..cd81e441 100644 --- a/app/controllers/actor_moderations_controller.rb +++ b/app/controllers/actor_moderations_controller.rb @@ -64,7 +64,7 @@ class ActorModerationsController < ApplicationController end end - message = actor_moderation.public_send(method) ? :success : :error + message = actor_moderations.public_send(method) ? :success : :error flash[message] = I18n.t("actor_moderations.action_on_several.#{message}") end diff --git a/app/jobs/activity_pub/instance_moderation_job.rb b/app/jobs/activity_pub/instance_moderation_job.rb index 214f8dd4..00100abf 100644 --- a/app/jobs/activity_pub/instance_moderation_job.rb +++ b/app/jobs/activity_pub/instance_moderation_job.rb @@ -30,6 +30,8 @@ class ActivityPub else scope.block_all_without_callbacks! end + + ActivityPub::SyncListsJob.perform_later(site: site) end end end diff --git a/app/jobs/activity_pub/sync_lists_job.rb b/app/jobs/activity_pub/sync_lists_job.rb new file mode 100644 index 00000000..39f6bdc9 --- /dev/null +++ b/app/jobs/activity_pub/sync_lists_job.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +class ActivityPub + # Sincroniza las listas de bloqueo y permitidas con el estado actual + # de la base de datos. + class SyncListsJob < ApplicationJob + # Siempre correr al final + self.priority = 100 + + attr_reader :logs + + # Ejecuta todas las requests y consolida los posibles errores. + # + # @param site [Site] + def run(site:) + @logs = {} + + instance_scope = site.instance_moderations.joins(:instance) + actor_scope = site.actor_moderations.joins(:actor) + + blocklist = wildcardize(instance_scope.blocked.pluck(:hostname)) + actor_scope.blocked.distinct.pluck(:mention).compact + allowlist = wildcardize(instance_scope.allowed.pluck(:hostname)) + actor_scope.allowed.distinct.pluck(:mention).compact + pauselist = wildcardize(instance_scope.paused.pluck(:hostname)) + actor_scope.paused.distinct.pluck(:mention).compact + + if blocklist.present? + Rails.logger.info "Bloqueando: #{blocklist.join(', ')}" + process(:blocked) { site.social_inbox.allowlist.delete(list: blocklist) } + process(:blocked) { site.social_inbox.blocklist.post(list: blocklist) } + end + + if allowlist.present? + Rails.logger.info "Permitiendo: #{allowlist.join(', ')}" + process(:allowed) { site.social_inbox.blocklist.delete(list: allowlist) } + process(:allowed) { site.social_inbox.allowlist.post(list: allowlist) } + end + + if pauselist.present? + Rails.logger.info "Pausando: #{pauselist.join(', ')}" + process(:paused) { site.social_inbox.blocklist.delete(list: pauselist) } + process(:paused) { site.social_inbox.allowlist.delete(list: pauselist) } + end + + # Si alguna falló, reintentar + raise if logs.present? + rescue Exception => e + ExceptionNotifier.notify_exception(e, data: { site: site.name, logs: logs, blocklist: blocklist, allowlist: allowlist, pauselist: pauselist }) + + raise + end + + private + + def process(stage) + response = yield + + return if response.ok? + + logs[stage] ||= [] + logs[stage] << { body: response.body, code: response.code } + end + + # @params hostnames [Array] + # @return [Array] + def wildcardize(hostnames) + hostnames.map do |hostname| + "@*@#{hostname}" + end + end + end +end diff --git a/app/models/actor_moderation.rb b/app/models/actor_moderation.rb index c566b844..18149be4 100644 --- a/app/models/actor_moderation.rb +++ b/app/models/actor_moderation.rb @@ -14,9 +14,9 @@ class ActorModeration < ApplicationRecord accepts_nested_attributes_for :remote_flag aasm do - state :paused, initial: true, before_enter: :pause_remotely! - state :allowed, before_enter: :allow_remotely! - state :blocked, before_enter: :block_remotely! + state :paused, initial: true + state :allowed + state :blocked state :reported state :removed @@ -25,19 +25,19 @@ class ActorModeration < ApplicationRecord end event :pause do - transitions from: %i[allowed blocked reported], to: :paused + transitions from: %i[allowed blocked reported], to: :paused, after: :synchronize! end # Al permitir una cuenta no se permiten todos los comentarios # pendientes de moderación que ya hizo. event :allow do - transitions from: %i[paused blocked reported], to: :allowed + transitions from: %i[paused blocked reported], to: :allowed, after: :synchronize! end # Al bloquear una cuenta no se bloquean todos los comentarios # pendientes de moderación que hizo. event :block do - transitions from: %i[paused allowed], to: :blocked + transitions from: %i[paused allowed], to: :blocked, after: :synchronize! end # Al reportar, necesitamos asociar una RemoteFlag para poder @@ -64,24 +64,7 @@ class ActorModeration < ApplicationRecord # Definir eventos en masa include AasmEventsConcern - def pause_remotely! - raise unless - actor.mention && - site.social_inbox.allowlist.delete(list: [actor.mention]).ok? && - site.social_inbox.blocklist.delete(list: [actor.mention]).ok? - end - - def allow_remotely! - raise unless - actor.mention && - site.social_inbox.allowlist.post(list: [actor.mention]).ok? && - site.social_inbox.blocklist.delete(list: [actor.mention]).ok? - end - - def block_remotely! - raise unless - actor.mention && - site.social_inbox.allowlist.delete(list: [actor.mention]).ok? && - site.social_inbox.blocklist.post(list: [actor.mention]).ok? + def synchronize! + ActivityPub::SyncListsJob.perform_later(site: site) end end diff --git a/app/models/fediblock_state.rb b/app/models/fediblock_state.rb index a6faa14f..82912f76 100644 --- a/app/models/fediblock_state.rb +++ b/app/models/fediblock_state.rb @@ -20,8 +20,8 @@ class FediblockState < ApplicationRecord # Aunque queramos las listas habilitadas por defecto, tenemos que # habilitarlas luego de crearlas para poder generar la lista de # bloqueo en la Social Inbox. - state :disabled, initial: true, before_enter: :disable_remotely_and_pause_instances! - state :enabled, before_enter: :enable_remotely_and_block_instances! + state :disabled, initial: true, before_enter: :pause_unique_instances! + state :enabled, before_enter: :block_instances! error_on_all_events do |e| ExceptionNotifier.notify_exception(e, data: { site: site.name, fediblock: id }) @@ -38,32 +38,27 @@ class FediblockState < ApplicationRecord # pero esto implica que tenemos que encontrar las que sí y quitarlas # de list_names event :disable do - transitions from: :enabled, to: :disabled + transitions from: :enabled, to: :disabled, after: :synchronize! end end private - def enable_remotely_and_block_instances! - # Bloquear todos las instancias de este Fediblock - enable_remotely! list_names(fediblock.hostnames) - - # Luego esta tarea crea las que falten e ignora las que ya se - # bloquearon. - ActivityPub::InstanceModerationJob.perform_now(site: site, hostnames: fediblock.hostnames, perform_remotely: false) + def block_instances! + ActivityPub::InstanceModerationJob.perform_later(site: site, hostnames: fediblock.hostnames, perform_remotely: false) end - def disable_remotely_and_pause_instances! - # Deshabilitar todas las instancias que no estén habilitadas por - # otros fediblocks - disable_remotely! list_names(unique_hostnames) - - # Pausar todas las moderaciones de las instancias que no estén - # bloqueadas por otros fediblocks. + # Pausar todas las moderaciones de las instancias que no estén + # bloqueadas por otros fediblocks. + def pause_unique_instances! instance_ids = ActivityPub::Instance.where(hostname: unique_hostnames).ids site.instance_moderations.where(instance_id: instance_ids).pause_all_without_callbacks! end + def synchronize! + ActivityPub::SyncListsJob.perform_later(site: site) + end + # Devuelve los hostnames únicos a esta instancia. # # @return [Array] @@ -82,30 +77,4 @@ class FediblockState < ApplicationRecord fediblock.hostnames - other_enabled_hostnames end end - - # @param hostnames [Array] - # @return [Array] - def list_names(hostnames) - hostnames.map do |hostname| - "@*@#{hostname}" - end - end - - # Al deshabilitar, las instancias pasan a ser analizadas caso por caso - # - # @param list [Array] - def disable_remotely!(list) - raise unless - site.social_inbox.blocklist.delete(list: list).ok? && - site.social_inbox.allowlist.delete(list: list).ok? - end - - # Al habilitar, se bloquean todas las instancias de la lista - # - # @param list [Array] - def enable_remotely!(list) - raise unless - site.social_inbox.blocklist.post(list: list).ok? && - site.social_inbox.allowlist.delete(list: list).ok? - end end diff --git a/app/models/instance_moderation.rb b/app/models/instance_moderation.rb index 7c76262e..5a1a5ed6 100644 --- a/app/models/instance_moderation.rb +++ b/app/models/instance_moderation.rb @@ -11,14 +11,18 @@ class InstanceModeration < ApplicationRecord belongs_to :instance, class_name: 'ActivityPub::Instance' aasm do - state :paused, initial: true, before_enter: :pause_remotely! - state :allowed, before_enter: :allow_remotely! - state :blocked, before_enter: :block_remotely! + state :paused, initial: true + state :allowed + state :blocked error_on_all_events do |e| ExceptionNotifier.notify_exception(e, data: { site: site.name, instance: instance.hostname, instance_moderation: id }) end + after_all_events do + ActivityPub::SyncListsJob.perform_later(site: site) + end + # Al volver la instancia a pausa no cambiamos el estado de # moderación de actores pre-existente. event :pause do @@ -40,36 +44,4 @@ class InstanceModeration < ApplicationRecord # Definir eventos en masa include AasmEventsConcern - - # @return [Array] - def actor_ids - ActivityPub::Actor.where(instance_id: instance_id).ids - end - - # Elimina la instancia de todas las listas - # - # @return [Boolean] - def pause_remotely! - raise unless - site.social_inbox.blocklist.delete(list: [instance.list_name]).ok? && - site.social_inbox.allowlist.delete(list: [instance.list_name]).ok? - end - - # Deja de permitir la instancia - # - # @return [Boolean] - def block_remotely! - raise unless - site.social_inbox.allowlist.delete(list: [instance.list_name]).ok? && - site.social_inbox.blocklist.post(list: [instance.list_name]).ok? - end - - # Permite la instancia - # - # @return [Boolean] - def allow_remotely! - raise unless - site.social_inbox.blocklist.delete(list: [instance.list_name]).ok? && - site.social_inbox.allowlist.post(list: [instance.list_name]).ok? - end end From a65a9f2f504969755529bd51efc03934c4c51a81 Mon Sep 17 00:00:00 2001 From: f Date: Sat, 16 Mar 2024 19:16:30 -0300 Subject: [PATCH 084/133] feat: aprobar o rechazar en segundo plano --- app/jobs/activity_pub/inbox_job.rb | 16 ++++++++++++++++ app/models/activity_pub.rb | 23 ++++++++++------------- 2 files changed, 26 insertions(+), 13 deletions(-) create mode 100644 app/jobs/activity_pub/inbox_job.rb diff --git a/app/jobs/activity_pub/inbox_job.rb b/app/jobs/activity_pub/inbox_job.rb new file mode 100644 index 00000000..93216d44 --- /dev/null +++ b/app/jobs/activity_pub/inbox_job.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class ActivityPub + class InboxJob < ApplicationJob + self.priority = 10 + + # @param :site [Site] + # @param :activity [String] + # @param :action [Symbol] + def perform(site:, activity:, action:) + response = site.social_inbox.inbox.public_send(action, id: activity) + + raise response.body unless response.ok? + end + end +end diff --git a/app/models/activity_pub.rb b/app/models/activity_pub.rb index 8142ecfb..55b48cd7 100644 --- a/app/models/activity_pub.rb +++ b/app/models/activity_pub.rb @@ -47,9 +47,9 @@ class ActivityPub < ApplicationRecord # Todavía no hay una decisión sobre el objeto state :paused, initial: true # Le usuarie aprobó el objeto - state :approved, before_enter: :allow_remotely! + state :approved # Le usuarie rechazó el objeto - state :rejected, before_enter: :reject_remotely! + state :rejected # Le usuarie reportó el objeto state :reported # Le actore eliminó el objeto @@ -84,11 +84,19 @@ class ActivityPub < ApplicationRecord # webhook a modo de confirmación. event :approve do transitions from: %i[paused], to: :approved + + after do + ActivityPub::InboxJob.perform_later(site: site, activity: activities.first.uri, action: :accept) + end end # La actividad fue rechazada event :reject do transitions from: %i[paused], to: :rejected + + after do + ActivityPub::InboxJob.perform_later(site: site, activity: activities.first.uri, action: :reject) + end end # Solo podemos reportarla luego de rechazarla @@ -103,15 +111,4 @@ class ActivityPub < ApplicationRecord # Definir eventos en masa include AasmEventsConcern - - # Lo que tenemos que aprobar o rechazar es la última actividad - # disponible, que según el scope por defecto, va a ser la primera de - # la lista. - def reject_remotely! - raise unless site.social_inbox.inbox.reject(id: activities.first.uri).ok? - end - - def allow_remotely! - raise unless site.social_inbox.inbox.accept(id: activities.first.uri).ok? - end end From e8ce721af02e4aade19dc92a2047def267fcb4fe Mon Sep 17 00:00:00 2001 From: f Date: Sat, 16 Mar 2024 19:20:32 -0300 Subject: [PATCH 085/133] =?UTF-8?q?fix:=20traducci=C3=B3n=20faltante?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/locales/es.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/es.yml b/config/locales/es.yml index 7f71781c..39d35afe 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -109,7 +109,7 @@ es: instances_btn_box: text_pause: Moderar caso por caso text_allow: Permitir todo - text_deny: Bloquear instancia + text_block: Bloquear instancia profiles_btn_box: text_pause: Revisar siempre text_allow: Aprobar siempre From 883da8d881fc1480cc40370ffdcedf9e6556bdf4 Mon Sep 17 00:00:00 2001 From: f Date: Sun, 17 Mar 2024 12:06:55 -0300 Subject: [PATCH 086/133] =?UTF-8?q?fix:=20quiz=C3=A1s=20no=20hay=20actor?= =?UTF-8?q?=20aun=20(=3F)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/jobs/activity_pub/fetch_job.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/jobs/activity_pub/fetch_job.rb b/app/jobs/activity_pub/fetch_job.rb index e9220bfc..42b2cb27 100644 --- a/app/jobs/activity_pub/fetch_job.rb +++ b/app/jobs/activity_pub/fetch_job.rb @@ -9,6 +9,8 @@ # autenticación. class ActivityPub class FetchJob < ApplicationJob + include Que::Unique + self.priority = 50 def perform(site:, object_id:) @@ -32,7 +34,7 @@ class ActivityPub return if current_type == object.type object = ::ActivityPub::Object.find(object_id) - object.actor.save if object.actor_type? + object.actor&.save if object.actor_type? # Arreglar las relaciones con actividades también ActivityPub.where(object_id: object.id).update_all(object_type: object.type) From bdb5175fcd96c02816bee1be498ccdd80d733486 Mon Sep 17 00:00:00 2001 From: f Date: Sun, 17 Mar 2024 12:10:07 -0300 Subject: [PATCH 087/133] =?UTF-8?q?fixup!=20fix:=20quiz=C3=A1s=20no=20hay?= =?UTF-8?q?=20actor=20aun=20(=3F)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/jobs/activity_pub/fetch_job.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/jobs/activity_pub/fetch_job.rb b/app/jobs/activity_pub/fetch_job.rb index 42b2cb27..bdb98e66 100644 --- a/app/jobs/activity_pub/fetch_job.rb +++ b/app/jobs/activity_pub/fetch_job.rb @@ -9,8 +9,6 @@ # autenticación. class ActivityPub class FetchJob < ApplicationJob - include Que::Unique - self.priority = 50 def perform(site:, object_id:) From 1623d618ee8743ada8d91d6c2ba2a670d20a5b53 Mon Sep 17 00:00:00 2001 From: f Date: Mon, 18 Mar 2024 11:21:13 -0300 Subject: [PATCH 088/133] fix: mostrar link externo al comentario #15638 --- app/models/activity_pub.rb | 36 ++++++++++++++++++++++++ app/views/moderation_queue/_comment.haml | 7 +++-- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/app/models/activity_pub.rb b/app/models/activity_pub.rb index 55b48cd7..913ac67d 100644 --- a/app/models/activity_pub.rb +++ b/app/models/activity_pub.rb @@ -43,6 +43,42 @@ class ActivityPub < ApplicationRecord end end + # Obtiene el campo `url` de diversas formas. Si es una String, asumir + # que es una URL, si es un Hash, asumir que es un Link, si es un + # Array de Strings, obtener la primera, si es de Hash, obtener el + # primer link con rel=canonical y mediaType=text/html + # + # De lo contrario devolver el ID. + # + # @todo Refactorizar + # @param object [Hash] + # @return [String] + def self.url_from_object(object) + raise unless object.respond_to?(:[]) + + url = + case object['url'] + when String then object['url'] + when Hash then object['href'] + # Esto es un lío porque queremos saber si es un Array o + # Array o mezcla y obtener el que más nos convenga o + # adivinar uno. + when Array + links = object['url'].map.with_index do |link, i| + case link + when Hash then link + else { 'href' => link.to_s } + end + end + + links.find do |link| + link['rel'] == 'canonical' && link['mediaType'] == 'text/html' + end&.[]('href') || links.first&.[]('href') + end + + url || object['id'] + end + aasm do # Todavía no hay una decisión sobre el objeto state :paused, initial: true diff --git a/app/views/moderation_queue/_comment.haml b/app/views/moderation_queue/_comment.haml index c6f6fd5c..787ae7c2 100644 --- a/app/views/moderation_queue/_comment.haml +++ b/app/views/moderation_queue/_comment.haml @@ -8,6 +8,8 @@ - in_reply_to = text_plain comment['inReplyTo'] - summary = text_plain comment['summary'] +-# @todo Generar un desplegable con todas las opciones +- url = text_plain ActivityPub.url_from_object(comment) .row.no-gutters .col-1 @@ -17,8 +19,9 @@ .d-flex.flex-row.align-items-center.justify-content-between %h4.mb-0 %a{ href: text_plain(comment['attributedTo']) }= text_plain profile['preferredUsername'] - %small - = render 'layouts/time', time: text_plain(comment['published']) + %a{ href: url } + %small + = render 'layouts/time', time: text_plain(comment['published']) - if in_reply_to.present? %dl %dt.d-inline From 7b87aaa93daf1d58a0b97ab89573043d239c4035 Mon Sep 17 00:00:00 2001 From: f Date: Mon, 18 Mar 2024 11:55:38 -0300 Subject: [PATCH 089/133] feat: referenciar objetos #15642 --- app/models/activity_pub/object.rb | 9 +++++++++ app/views/moderation_queue/_comment.haml | 9 +++++++++ app/views/moderation_queue/_comments.haml | 2 +- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/app/models/activity_pub/object.rb b/app/models/activity_pub/object.rb index 9061c4c5..1d5d4478 100644 --- a/app/models/activity_pub/object.rb +++ b/app/models/activity_pub/object.rb @@ -29,5 +29,14 @@ class ActivityPub def object_type? true end + + # Poder explorar propiedades remotas + # + # @return [DistributedPress::V1::Social::ReferencedObject] + def referenced(site) + require 'distributed_press/v1/social/referenced_object' + + @referenced ||= DistributedPress::V1::Social::ReferencedObject.new(object: content, dereferencer: site.social_inbox.dereferencer) + end end end diff --git a/app/views/moderation_queue/_comment.haml b/app/views/moderation_queue/_comment.haml index 787ae7c2..10f09106 100644 --- a/app/views/moderation_queue/_comment.haml +++ b/app/views/moderation_queue/_comment.haml @@ -1,12 +1,21 @@ -# Componente Comentario + @param site [Site] @param form [String] @param profile [Hash] @param comment [Hash] @param activity_pub [ActivityPub] - in_reply_to = text_plain comment['inReplyTo'] +:ruby + begin + if in_reply_to && (remote_object = object.referenced(site)['inReplyTo']) + in_reply_to = ActivityPub.url_from_object(remote_object) + end + rescue Exception => e + ExceptionNotifier.notify_exception(e, data: { site: site.name, object: comment }) + end - summary = text_plain comment['summary'] -# @todo Generar un desplegable con todas las opciones - url = text_plain ActivityPub.url_from_object(comment) diff --git a/app/views/moderation_queue/_comments.haml b/app/views/moderation_queue/_comments.haml index 68671f9e..583ef511 100644 --- a/app/views/moderation_queue/_comments.haml +++ b/app/views/moderation_queue/_comments.haml @@ -13,4 +13,4 @@ %h4= t('moderation_queue.nothing') - moderation_queue.each do |activity_pub| %hr - = render 'moderation_queue/comment', comment: activity_pub.object.content, profile: activity_pub.actor.content, activity_pub: activity_pub, form: form_id + = render 'moderation_queue/comment', comment: activity_pub.object.content, profile: activity_pub.actor.content, activity_pub: activity_pub, form: form_id, site: site, object: activity_pub.object From 9dc0ed7684ac56c456283baaabc678358abcda67 Mon Sep 17 00:00:00 2001 From: f Date: Mon, 18 Mar 2024 12:48:09 -0300 Subject: [PATCH 090/133] fix: reusar el reporto remoto #15648 --- app/controllers/concerns/moderation_concern.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/controllers/concerns/moderation_concern.rb b/app/controllers/concerns/moderation_concern.rb index 3b9d818f..8340ec2a 100644 --- a/app/controllers/concerns/moderation_concern.rb +++ b/app/controllers/concerns/moderation_concern.rb @@ -16,7 +16,9 @@ module ModerationConcern end def remote_flag_params(model) - { remote_flag_attributes: { id: model.remote_flag_id, message: ''.dup } }.tap do |p| + remote_flag = ActivityPub::RemoteFlag.find_by(actor_id: model.actor_id) + + { remote_flag_attributes: { id: remote_flag&.id, message: ''.dup } }.tap do |p| p[:remote_flag_attributes][:site_id] = model.site_id p[:remote_flag_attributes][:actor_id] = model.actor_id From bcfb7a5a7ffb3deec681767c54923244c47da0f9 Mon Sep 17 00:00:00 2001 From: f Date: Mon, 18 Mar 2024 12:55:27 -0300 Subject: [PATCH 091/133] fix: asignar la remote flag #15648 --- app/controllers/activity_pubs_controller.rb | 7 ++++++- app/controllers/actor_moderations_controller.rb | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/controllers/activity_pubs_controller.rb b/app/controllers/activity_pubs_controller.rb index 225311c2..d5041a84 100644 --- a/app/controllers/activity_pubs_controller.rb +++ b/app/controllers/activity_pubs_controller.rb @@ -8,7 +8,12 @@ class ActivityPubsController < ApplicationController define_method(event) do authorize activity_pub - activity_pub.update(remote_flag_params(activity_pub)) if event == :report + if event == :report + remote_flag_params(activity_pub).tap do |p| + activity_pub.remote_flag_id = p[:remote_flag_attributes][:id] + activity_pub.update(p) + end + end message = if activity_pub.public_send(:"may_#{event}?") && activity_pub.public_send(:"#{event}!") diff --git a/app/controllers/actor_moderations_controller.rb b/app/controllers/actor_moderations_controller.rb index cd81e441..70aaf992 100644 --- a/app/controllers/actor_moderations_controller.rb +++ b/app/controllers/actor_moderations_controller.rb @@ -15,7 +15,12 @@ class ActorModerationsController < ApplicationController authorize actor_moderation # Crea una RemoteFlag si se envían los parámetros adecuados - actor_moderation.update(remote_flag_params(actor_moderation)) if actor_event == :report + if actor_event == :report + remote_flag_params(actor_moderation).tap do |p| + actor_moderation.remote_flag_id = p[:remote_flag_attributes][:id] + actor_moderation.update(p) + end + end message = if actor_moderation.public_send(:"may_#{actor_event}?") && actor_moderation.public_send(:"#{actor_event}!") From ceeb0009ef2fcd1faa01d5e59f2f77c2a0f124cb Mon Sep 17 00:00:00 2001 From: f Date: Mon, 18 Mar 2024 14:40:11 -0300 Subject: [PATCH 092/133] fix: descubrir el tipo de objeto siempre closes #15656 closes #15657 closes #15658 closes #15660 closes #15661 closes #15662 closes #15663 closes #15664 closes #15665 closes #15666 closes #15667 closes #15668 closes #15669 closes #15670 closes #15671 closes #15672 closes #15673 closes #15674 closes #15675 closes #15676 closes #15677 closes #15678 closes #15679 closes #15680 closes #15681 closes #15682 closes #15683 closes #15684 closes #15685 closes #15686 closes #15687 closes #15688 closes #15689 closes #15690 closes #15691 closes #15692 closes #15693 closes #15694 closes #15695 closes #15696 closes #15697 closes #15698 closes #15699 closes #15700 closes #15701 closes #15702 closes #15704 closes #15705 closes #15706 closes #15707 closes #15708 --- app/jobs/activity_pub/process_job.rb | 13 +------------ app/models/activity_pub/object.rb | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/app/jobs/activity_pub/process_job.rb b/app/jobs/activity_pub/process_job.rb index 6554b44d..9b72be43 100644 --- a/app/jobs/activity_pub/process_job.rb +++ b/app/jobs/activity_pub/process_job.rb @@ -64,18 +64,7 @@ class ActivityPub # @return [ActivityPub::Object] def object @object ||= ::ActivityPub::Object.find_or_initialize_by(uri: object_uri).tap do |o| - # XXX: Si el objeto es una actividad, esto siempre va a ser - # Generic - o.type ||= 'ActivityPub::Object::Generic' - - if object_embedded? - o.content = original_object - begin - type = original_object[:type].presence - o.type = "ActivityPub::Object::#{type}".constantize if type - rescue NameError - end - end + o.content = original_object if object_embedded? o.save! diff --git a/app/models/activity_pub/object.rb b/app/models/activity_pub/object.rb index 1d5d4478..16cd6b01 100644 --- a/app/models/activity_pub/object.rb +++ b/app/models/activity_pub/object.rb @@ -5,6 +5,8 @@ class ActivityPub class Object < ApplicationRecord include ActivityPub::Concerns::JsonLdConcern + before_validation :type_from_content!, unless: :type? + # Los objetos son únicos a toda la base de datos validates :uri, presence: true, url: true, uniqueness: true @@ -38,5 +40,20 @@ class ActivityPub @referenced ||= DistributedPress::V1::Social::ReferencedObject.new(object: content, dereferencer: site.social_inbox.dereferencer) end + + private + + # Encuentra el tipo a partir del contenido, si existe. + # + # XXX: Si el objeto es una actividad, esto siempre va a ser + # Generic + def type_from_content! + self.type = + begin + "ActivityPub::Object::#{content['type'].presence || 'Generic'}".constantize + rescue NameError + ActivityPub::Object::Generic + end + end end end From e50ae70ebb7c254cc87769419a31b46582823c1e Mon Sep 17 00:00:00 2001 From: f Date: Mon, 18 Mar 2024 15:06:41 -0300 Subject: [PATCH 093/133] fix: no actualizar si el contenido estaba cacheado --- app/jobs/activity_pub/fetch_job.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/jobs/activity_pub/fetch_job.rb b/app/jobs/activity_pub/fetch_job.rb index bdb98e66..78a6dbee 100644 --- a/app/jobs/activity_pub/fetch_job.rb +++ b/app/jobs/activity_pub/fetch_job.rb @@ -22,7 +22,9 @@ class ActivityPub # @todo Fallar cuando la respuesta no funcione? return unless response.ok? - return if response.miss? && object.content.present? + # Ignorar si ya la caché fue revalidada y ya teníamos el + # contenido + return if response.hit? && object.content.present? current_type = object.type content = FastJsonparser.parse(response.body) From e6d4b4d3f16ff9f114e04530c1104d28df0fc0e6 Mon Sep 17 00:00:00 2001 From: f Date: Mon, 18 Mar 2024 15:10:16 -0300 Subject: [PATCH 094/133] fix: validar que el contenido del objeto sea el que queremos --- app/models/activity_pub/object.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/models/activity_pub/object.rb b/app/models/activity_pub/object.rb index 16cd6b01..d37c9b88 100644 --- a/app/models/activity_pub/object.rb +++ b/app/models/activity_pub/object.rb @@ -9,6 +9,7 @@ class ActivityPub # Los objetos son únicos a toda la base de datos validates :uri, presence: true, url: true, uniqueness: true + validate :uri_is_content_id?, if: :content? has_many :activity_pubs, as: :object @@ -43,6 +44,12 @@ class ActivityPub private + def uri_is_content_id? + return if self.uri == content['id'] + + errors.add(:activity_pub_objects, 'El ID del objeto no coincide con su URI') + end + # Encuentra el tipo a partir del contenido, si existe. # # XXX: Si el objeto es una actividad, esto siempre va a ser From 75b6314e1dfc70cf53e716914b211664ee3dd768 Mon Sep 17 00:00:00 2001 From: f Date: Mon, 18 Mar 2024 15:46:03 -0300 Subject: [PATCH 095/133] =?UTF-8?q?fix:=20qu=C3=A9=20pasa=20con=20los=20ob?= =?UTF-8?q?jetos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/jobs/activity_pub/fetch_job.rb | 7 ++++--- .../20240318183846_fix_duplicate_objects.rb | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 db/migrate/20240318183846_fix_duplicate_objects.rb diff --git a/app/jobs/activity_pub/fetch_job.rb b/app/jobs/activity_pub/fetch_job.rb index 78a6dbee..f1a51e96 100644 --- a/app/jobs/activity_pub/fetch_job.rb +++ b/app/jobs/activity_pub/fetch_job.rb @@ -29,15 +29,16 @@ class ActivityPub current_type = object.type content = FastJsonparser.parse(response.body) - object.update(content: content, type: ActivityPub::Object.type_from(content).name) + object.update!(content: content, type: ActivityPub::Object.type_from(content).name) return if current_type == object.type object = ::ActivityPub::Object.find(object_id) - object.actor&.save if object.actor_type? + # Actualiza la mención + object.actor&.save! if object.actor_type? # Arreglar las relaciones con actividades también - ActivityPub.where(object_id: object.id).update_all(object_type: object.type) + ActivityPub.where(object_id: object.id).update_all(object_type: object.type, updated_at: Time.now) rescue FastJsonparser::ParseError => e ExceptionNotifier.notify_exception(e, data: { site: site.name, body: response.body }) end diff --git a/db/migrate/20240318183846_fix_duplicate_objects.rb b/db/migrate/20240318183846_fix_duplicate_objects.rb new file mode 100644 index 00000000..f863ba3e --- /dev/null +++ b/db/migrate/20240318183846_fix_duplicate_objects.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +# De alguna forma se guardaron objetos duplicados! +class FixDuplicateObjects < ActiveRecord::Migration[6.1] + def up + ActivityPub::Object.group(:uri).count.select { |_, v| v > 1 }.keys.each do |uri| + objects = ActivityPub::Object.where(uri: uri) + deleted_ids = objects[1..].map(&:delete).map(&:id) + + ActivityPub.where(object_id: deleted_ids).update_all(object_id: object.first.id, updated_at: Time.now) + end + end + + def down; end +end From bd0df0a53a5a0c18cd9bcfcd0e89c8ecf569d0a0 Mon Sep 17 00:00:00 2001 From: f Date: Mon, 18 Mar 2024 15:47:38 -0300 Subject: [PATCH 096/133] =?UTF-8?q?fixup!=20fix:=20qu=C3=A9=20pasa=20con?= =?UTF-8?q?=20los=20objetos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db/migrate/20240318183846_fix_duplicate_objects.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrate/20240318183846_fix_duplicate_objects.rb b/db/migrate/20240318183846_fix_duplicate_objects.rb index f863ba3e..88d23c6f 100644 --- a/db/migrate/20240318183846_fix_duplicate_objects.rb +++ b/db/migrate/20240318183846_fix_duplicate_objects.rb @@ -7,7 +7,7 @@ class FixDuplicateObjects < ActiveRecord::Migration[6.1] objects = ActivityPub::Object.where(uri: uri) deleted_ids = objects[1..].map(&:delete).map(&:id) - ActivityPub.where(object_id: deleted_ids).update_all(object_id: object.first.id, updated_at: Time.now) + ActivityPub.where(object_id: deleted_ids).update_all(object_id: objects.first.id, updated_at: Time.now) end end From 376339aee50cd7e7353a2212c9aa8f4f68965581 Mon Sep 17 00:00:00 2001 From: f Date: Mon, 18 Mar 2024 16:45:00 -0300 Subject: [PATCH 097/133] fix: correr las consultas de todas formas --- app/jobs/activity_pub/fetch_job.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/jobs/activity_pub/fetch_job.rb b/app/jobs/activity_pub/fetch_job.rb index f1a51e96..4d4d4483 100644 --- a/app/jobs/activity_pub/fetch_job.rb +++ b/app/jobs/activity_pub/fetch_job.rb @@ -21,6 +21,7 @@ class ActivityPub response = site.social_inbox.dereferencer.get(uri: object.uri) # @todo Fallar cuando la respuesta no funcione? + # @todo Eliminar en 410 Gone return unless response.ok? # Ignorar si ya la caché fue revalidada y ya teníamos el # contenido @@ -31,8 +32,6 @@ class ActivityPub object.update!(content: content, type: ActivityPub::Object.type_from(content).name) - return if current_type == object.type - object = ::ActivityPub::Object.find(object_id) # Actualiza la mención object.actor&.save! if object.actor_type? From 8034d206121c6ef7e1aecca48f2174e075af6426 Mon Sep 17 00:00:00 2001 From: f Date: Mon, 18 Mar 2024 16:55:20 -0300 Subject: [PATCH 098/133] fix: ignorar comentarios y cuentas sin contenido aun --- app/views/moderation_queue/_accounts.haml | 1 + app/views/moderation_queue/_comments.haml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/app/views/moderation_queue/_accounts.haml b/app/views/moderation_queue/_accounts.haml index d8d76f0d..257b0fbf 100644 --- a/app/views/moderation_queue/_accounts.haml +++ b/app/views/moderation_queue/_accounts.haml @@ -12,5 +12,6 @@ - if actor_moderations.count.zero? %h4= t('moderation_queue.nothing') - actor_moderations.find_each do |actor_moderation| + - next if actor_moderation.actor.content.empty? %hr = render 'account', actor_moderation: actor_moderation, profile: actor_moderation.actor.content, form: form_id diff --git a/app/views/moderation_queue/_comments.haml b/app/views/moderation_queue/_comments.haml index 583ef511..a7523517 100644 --- a/app/views/moderation_queue/_comments.haml +++ b/app/views/moderation_queue/_comments.haml @@ -12,5 +12,7 @@ - if moderation_queue.count.zero? %h4= t('moderation_queue.nothing') - moderation_queue.each do |activity_pub| + - next if activity_pub.object.content.empty? + - next if activity_pub.actor.content.empty? %hr = render 'moderation_queue/comment', comment: activity_pub.object.content, profile: activity_pub.actor.content, activity_pub: activity_pub, form: form_id, site: site, object: activity_pub.object From ab43c84ab1b0287a49150b9ab5011f61b76fc151 Mon Sep 17 00:00:00 2001 From: f Date: Mon, 18 Mar 2024 17:13:32 -0300 Subject: [PATCH 099/133] fix: indicar el filtro activo #15652 --- app/helpers/moderation_queue_helper.rb | 10 +++++++++- app/views/components/_comments_show_submenu.haml | 3 ++- app/views/components/_dropdown_item.haml | 3 ++- app/views/components/_instances_show_submenu.haml | 3 ++- app/views/components/_profiles_show_submenu.haml | 3 ++- 5 files changed, 17 insertions(+), 5 deletions(-) diff --git a/app/helpers/moderation_queue_helper.rb b/app/helpers/moderation_queue_helper.rb index 3681bec3..c69364ae 100644 --- a/app/helpers/moderation_queue_helper.rb +++ b/app/helpers/moderation_queue_helper.rb @@ -2,6 +2,14 @@ module ModerationQueueHelper def filter_states(**args) - params.permit(:state, :actor_state, :activity_pub_state).merge(**args) + params.permit(:instance_state, :actor_state, :activity_pub_state).merge(**args) + end + + def active?(states, state_name, state) + if params[state_name].present? + params[state_name] == state.to_s + else + states.first == state + end end end diff --git a/app/views/components/_comments_show_submenu.haml b/app/views/components/_comments_show_submenu.haml index 60c02501..9964a62a 100644 --- a/app/views/components/_comments_show_submenu.haml +++ b/app/views/components/_comments_show_submenu.haml @@ -1,4 +1,5 @@ - ActivityPub.states.each do |state| = render 'components/dropdown_item', text: t(".submenu_#{state}", count: activity_pubs.unscope(where: :aasm_state).public_send(state).count), - path: filter_states(activity_pub_state: state) + path: filter_states(activity_pub_state: state), + class: ('active' if active?(ActivityPub.states, :activity_pub_state, state)) diff --git a/app/views/components/_dropdown_item.haml b/app/views/components/_dropdown_item.haml index e5b16950..a4d363a8 100644 --- a/app/views/components/_dropdown_item.haml +++ b/app/views/components/_dropdown_item.haml @@ -1,4 +1,5 @@ -# @param :text [String] Contenido del link @param :path [String,Hash] Link -= link_to text, path, class: 'dropdown-item', data: { target: 'dropdown.item' } +- local_assigns[:class] = "dropdown-item #{local_assigns[:class]}" += link_to text, path, class: local_assigns[:class], data: { target: 'dropdown.item' } diff --git a/app/views/components/_instances_show_submenu.haml b/app/views/components/_instances_show_submenu.haml index c56df547..6b9b747e 100644 --- a/app/views/components/_instances_show_submenu.haml +++ b/app/views/components/_instances_show_submenu.haml @@ -1,4 +1,5 @@ - InstanceModeration.states.each do |state| = render 'components/dropdown_item', text: t(".submenu_#{state}", count: instance_moderations.unscope(where: :aasm_state).public_send(state).count), - path: filter_states(instance_state: state) + path: filter_states(instance_state: state), + class: ('active' if active?(InstanceModeration.states, :instance_state, state)) diff --git a/app/views/components/_profiles_show_submenu.haml b/app/views/components/_profiles_show_submenu.haml index 99694698..bebfbe20 100644 --- a/app/views/components/_profiles_show_submenu.haml +++ b/app/views/components/_profiles_show_submenu.haml @@ -1,4 +1,5 @@ - ActorModeration.states.each do |actor_state| = render 'components/dropdown_item', text: t(".submenu_#{actor_state}", count: actor_moderations.unscope(where: :aasm_state).public_send(actor_state).count), - path: filter_states(actor_state: actor_state) + path: filter_states(actor_state: actor_state), + class: ('active' if active?(ActorModeration.states, :actor_state, actor_state)) From dae0346f0ddc3d15825f7d4232b16e9fe86f2c15 Mon Sep 17 00:00:00 2001 From: f Date: Mon, 18 Mar 2024 17:28:49 -0300 Subject: [PATCH 100/133] fix: pasar el sitio como argumento --- app/views/actor_moderations/show.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/actor_moderations/show.haml b/app/views/actor_moderations/show.haml index 633c1be5..ca5764f4 100644 --- a/app/views/actor_moderations/show.haml +++ b/app/views/actor_moderations/show.haml @@ -5,4 +5,4 @@ .col-12.col-md-8 = render 'components/profiles_btn_box', actor_moderation: @actor_moderation .col-12.col-md-8 - = render 'moderation_queue/comments', moderation_queue: @moderation_queue + = render 'moderation_queue/comments', site: @site, moderation_queue: @moderation_queue From 7c9e9758c5fecd4deb09a08f337e95439e0469da Mon Sep 17 00:00:00 2001 From: f Date: Mon, 18 Mar 2024 17:51:29 -0300 Subject: [PATCH 101/133] =?UTF-8?q?fix:=20informar=20la=20uri=20que=20fall?= =?UTF-8?q?=C3=B3=20#15712?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/jobs/activity_pub/fetch_job.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/jobs/activity_pub/fetch_job.rb b/app/jobs/activity_pub/fetch_job.rb index 4d4d4483..9d14307a 100644 --- a/app/jobs/activity_pub/fetch_job.rb +++ b/app/jobs/activity_pub/fetch_job.rb @@ -39,7 +39,7 @@ class ActivityPub # Arreglar las relaciones con actividades también ActivityPub.where(object_id: object.id).update_all(object_type: object.type, updated_at: Time.now) rescue FastJsonparser::ParseError => e - ExceptionNotifier.notify_exception(e, data: { site: site.name, body: response.body }) + ExceptionNotifier.notify_exception(e, data: { site: site.name, object: object.uri, body: response.body }) end end end From e2abe224d40f7572244f4969cedf3af1bd1a65df Mon Sep 17 00:00:00 2001 From: f Date: Mon, 18 Mar 2024 17:54:07 -0300 Subject: [PATCH 102/133] fix: fallbacks #15659 --- app/controllers/actor_moderations_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/actor_moderations_controller.rb b/app/controllers/actor_moderations_controller.rb index 70aaf992..739b1f46 100644 --- a/app/controllers/actor_moderations_controller.rb +++ b/app/controllers/actor_moderations_controller.rb @@ -44,7 +44,7 @@ class ActorModerationsController < ApplicationController @moderation_queue = rubanok_process(site.activity_pubs.where(actor_id: actor_moderation.actor_id), with: ActivityPubProcessor) - breadcrumb @remote_profile['name'], '' + breadcrumb @remote_profile['name'] || actor_moderation.actor.mention || actor_moderation.actor.uri, '' end def action_on_several From d13e56e692e65344b436d5585289b9268fe43669 Mon Sep 17 00:00:00 2001 From: f Date: Mon, 18 Mar 2024 18:09:32 -0300 Subject: [PATCH 103/133] =?UTF-8?q?fix:=20descripci=C3=B3n=20para=20las=20?= =?UTF-8?q?listas=20de=20bloqueo=20#15643?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/locales/en.yml | 2 +- config/locales/es.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/locales/en.yml b/config/locales/en.yml index 6f76fe57..075b62ec 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -180,7 +180,7 @@ en: reply_to: Reply to instances: title: My block lists - description: Description + description: "Blocklists contain instances known for hosting hate speech, promote fascism, violence, sexual/gendered abuse and/or misinformation." custom_block: Custom block lists submit: Save block lists instance: diff --git a/config/locales/es.yml b/config/locales/es.yml index 39d35afe..2d4974b1 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -179,7 +179,7 @@ es: reply_to: En respuesta a instances: title: Mis listas de bloqueo - description: Descripción de listas de bloqueo + description: "Las listas de bloqueo contienen instancias conocidas por alojar discurso de odio, promover el fascismo, la violencia, abuso sexual y/o desinformación." custom_block: Lista personalizada de bloqueo submit: Guardar listas de bloqueo instance: From 979f3c1b3ac214856840a7bfa248603229dbdbfe Mon Sep 17 00:00:00 2001 From: f Date: Mon, 18 Mar 2024 18:12:02 -0300 Subject: [PATCH 104/133] fix: ayuda para la lista customizada #15643 --- app/views/moderation_queue/_block_instances_textarea.haml | 2 +- config/locales/en.yml | 3 +++ config/locales/es.yml | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/views/moderation_queue/_block_instances_textarea.haml b/app/views/moderation_queue/_block_instances_textarea.haml index 9729d4de..7daf0410 100644 --- a/app/views/moderation_queue/_block_instances_textarea.haml +++ b/app/views/moderation_queue/_block_instances_textarea.haml @@ -1,3 +1,3 @@ .form-group = label_tag 'custom_blocklist', t('moderation_queue.instances.custom_block') - = text_area_tag 'custom_blocklist', nil, class: 'form-control' + = text_area_tag 'custom_blocklist', nil, class: 'form-control', placeholder: t('moderation_queue.instances.custom_block_placeholder') diff --git a/config/locales/en.yml b/config/locales/en.yml index 075b62ec..35b857e6 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -182,6 +182,9 @@ en: title: My block lists description: "Blocklists contain instances known for hosting hate speech, promote fascism, violence, sexual/gendered abuse and/or misinformation." custom_block: Custom block lists + custom_block_placeholder: | + a.doma.in + per.li.ne submit: Save block lists instance: users: "Users:" diff --git a/config/locales/es.yml b/config/locales/es.yml index 2d4974b1..77e611ed 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -181,6 +181,9 @@ es: title: Mis listas de bloqueo description: "Las listas de bloqueo contienen instancias conocidas por alojar discurso de odio, promover el fascismo, la violencia, abuso sexual y/o desinformación." custom_block: Lista personalizada de bloqueo + custom_block_placeholder: | + un.domin.io + por.lin.ea submit: Guardar listas de bloqueo instance: users: "Usuaries:" From b90f8864461e3b2d8a5f725a0a81cd9f4cc858e7 Mon Sep 17 00:00:00 2001 From: f Date: Tue, 19 Mar 2024 09:47:24 -0300 Subject: [PATCH 105/133] feat: fedipact --- app/models/activity_pub/fediblock.rb | 4 +-- ...240319124212_add_fedipact_to_fediblocks.rb | 25 +++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20240319124212_add_fedipact_to_fediblocks.rb diff --git a/app/models/activity_pub/fediblock.rb b/app/models/activity_pub/fediblock.rb index 4abcb80f..ec66c032 100644 --- a/app/models/activity_pub/fediblock.rb +++ b/app/models/activity_pub/fediblock.rb @@ -31,8 +31,8 @@ class ActivityPub class FediblockDownloadError < ::StandardError; end - validates_presence_of :title, :url, :download_url, :format - validates_inclusion_of :format, in: %w[mastodon fediblock] + validates_presence_of :title, :url, :format + validates_inclusion_of :format, in: %w[mastodon fediblock none] HOSTNAME_HEADERS = { 'mastodon' => '#domain', diff --git a/db/migrate/20240319124212_add_fedipact_to_fediblocks.rb b/db/migrate/20240319124212_add_fedipact_to_fediblocks.rb new file mode 100644 index 00000000..648f2ee7 --- /dev/null +++ b/db/migrate/20240319124212_add_fedipact_to_fediblocks.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +# Agrega threads.net a las listas de bloqueo +class AddFedipactToFediblocks < ActiveRecord::Migration[6.1] + def up + fedipact = + ActivityPub::Fediblock.create( + hostnames: %w[threads.net], + title: 'Fedipact', + url: 'https://fedipact.online/', + format: 'none' + ) + + DeploySocialDistributedPress.find_each do |deploy| + FediblockState.create(site: deploy.site, fediblock: fedipact, aasm_state: 'disabled').tap do |f| + f.enable! + end + end + end + + def down + fedipact = ActivityPub::Fediblock.find_by(url: 'https://fedipact.online/').delete + FediblockState.where(fediblock_id: fedipact.id).delete_all + end +end From c047858a6f418a7bbf42eb5d3730a5ed114916c1 Mon Sep 17 00:00:00 2001 From: f Date: Tue, 19 Mar 2024 09:49:34 -0300 Subject: [PATCH 106/133] fixup! feat: fedipact --- db/migrate/20240319124212_add_fedipact_to_fediblocks.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/db/migrate/20240319124212_add_fedipact_to_fediblocks.rb b/db/migrate/20240319124212_add_fedipact_to_fediblocks.rb index 648f2ee7..d78439b2 100644 --- a/db/migrate/20240319124212_add_fedipact_to_fediblocks.rb +++ b/db/migrate/20240319124212_add_fedipact_to_fediblocks.rb @@ -3,6 +3,8 @@ # Agrega threads.net a las listas de bloqueo class AddFedipactToFediblocks < ActiveRecord::Migration[6.1] def up + change_column :activity_pub_fediblocks, :download_url, :string, null: true + fedipact = ActivityPub::Fediblock.create( hostnames: %w[threads.net], @@ -21,5 +23,6 @@ class AddFedipactToFediblocks < ActiveRecord::Migration[6.1] def down fedipact = ActivityPub::Fediblock.find_by(url: 'https://fedipact.online/').delete FediblockState.where(fediblock_id: fedipact.id).delete_all + change_column :activity_pub_fediblocks, :download_url, :string, null: false end end From 48d8d2d8d802b46318f6a36c6beb9a1955683fa5 Mon Sep 17 00:00:00 2001 From: f Date: Tue, 19 Mar 2024 10:59:10 -0300 Subject: [PATCH 107/133] =?UTF-8?q?fix:=20la=20l=C3=B3gica=20estaba=20al?= =?UTF-8?q?=20rev=C3=A9s=20#15647?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/jobs/activity_pub/remote_flag_job.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/jobs/activity_pub/remote_flag_job.rb b/app/jobs/activity_pub/remote_flag_job.rb index 20833bd4..71751d39 100644 --- a/app/jobs/activity_pub/remote_flag_job.rb +++ b/app/jobs/activity_pub/remote_flag_job.rb @@ -13,7 +13,7 @@ class ActivityPub self.priority = 30 def perform(remote_flag:) - return if remote_flag.may_queue? + return unless remote_flag.may_queue? inbox = remote_flag.actor&.content&.[]('inbox') From 93ebb6431a211d75f462c86163ec9fbc5e9ea73d Mon Sep 17 00:00:00 2001 From: f Date: Tue, 19 Mar 2024 11:20:49 -0300 Subject: [PATCH 108/133] =?UTF-8?q?fix:=20buscar=20un=20c=C3=B3digo=20de?= =?UTF-8?q?=20respuesta=20general?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/jobs/activity_pub/actor_fetch_job.rb | 2 +- app/jobs/activity_pub/fetch_job.rb | 2 +- app/jobs/activity_pub/inbox_job.rb | 2 +- app/jobs/activity_pub/instance_fetch_job.rb | 2 +- app/jobs/activity_pub/remote_flag_job.rb | 2 +- app/jobs/activity_pub/sync_lists_job.rb | 2 +- app/models/activity_pub/fediblock.rb | 2 +- app/models/deploy_social_distributed_press.rb | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/jobs/activity_pub/actor_fetch_job.rb b/app/jobs/activity_pub/actor_fetch_job.rb index 1190e936..598ecd83 100644 --- a/app/jobs/activity_pub/actor_fetch_job.rb +++ b/app/jobs/activity_pub/actor_fetch_job.rb @@ -16,7 +16,7 @@ class ActivityPub response = site.social_inbox.dereferencer.get(uri: actor.uri) # @todo Fallar cuando la respuesta no funcione? - return unless response.ok? + return unless response.success? return if response.miss? && actor.content.present? actor.object.update(content: FastJsonparser.parse(response.body)) diff --git a/app/jobs/activity_pub/fetch_job.rb b/app/jobs/activity_pub/fetch_job.rb index 9d14307a..beb585ff 100644 --- a/app/jobs/activity_pub/fetch_job.rb +++ b/app/jobs/activity_pub/fetch_job.rb @@ -22,7 +22,7 @@ class ActivityPub # @todo Fallar cuando la respuesta no funcione? # @todo Eliminar en 410 Gone - return unless response.ok? + return unless response.success? # Ignorar si ya la caché fue revalidada y ya teníamos el # contenido return if response.hit? && object.content.present? diff --git a/app/jobs/activity_pub/inbox_job.rb b/app/jobs/activity_pub/inbox_job.rb index 93216d44..cb807704 100644 --- a/app/jobs/activity_pub/inbox_job.rb +++ b/app/jobs/activity_pub/inbox_job.rb @@ -10,7 +10,7 @@ class ActivityPub def perform(site:, activity:, action:) response = site.social_inbox.inbox.public_send(action, id: activity) - raise response.body unless response.ok? + raise response.body unless response.success? end end end diff --git a/app/jobs/activity_pub/instance_fetch_job.rb b/app/jobs/activity_pub/instance_fetch_job.rb index 9c562f7d..dc84caf2 100644 --- a/app/jobs/activity_pub/instance_fetch_job.rb +++ b/app/jobs/activity_pub/instance_fetch_job.rb @@ -15,7 +15,7 @@ class ActivityPub response = site.social_inbox.dereferencer.get(uri: uri) - next unless response.ok? + next unless response.success? # @todo Validate schema next unless response.parsed_response.is_a?(DistributedPress::V1::Social::ReferencedObject) diff --git a/app/jobs/activity_pub/remote_flag_job.rb b/app/jobs/activity_pub/remote_flag_job.rb index 71751d39..f586a92e 100644 --- a/app/jobs/activity_pub/remote_flag_job.rb +++ b/app/jobs/activity_pub/remote_flag_job.rb @@ -25,7 +25,7 @@ class ActivityPub client = remote_flag.main_site.social_inbox.client_for(uri.origin) response = client.post(endpoint: uri.path, body: remote_flag.content) - raise 'No se pudo enviar el reporte' unless response.ok? + raise 'No se pudo enviar el reporte' unless response.success? remote_flag.report! rescue Exception => e diff --git a/app/jobs/activity_pub/sync_lists_job.rb b/app/jobs/activity_pub/sync_lists_job.rb index 39f6bdc9..cc500bee 100644 --- a/app/jobs/activity_pub/sync_lists_job.rb +++ b/app/jobs/activity_pub/sync_lists_job.rb @@ -53,7 +53,7 @@ class ActivityPub def process(stage) response = yield - return if response.ok? + return if response.success? logs[stage] ||= [] logs[stage] << { body: response.body, code: response.code } diff --git a/app/models/activity_pub/fediblock.rb b/app/models/activity_pub/fediblock.rb index ec66c032..17897d79 100644 --- a/app/models/activity_pub/fediblock.rb +++ b/app/models/activity_pub/fediblock.rb @@ -52,7 +52,7 @@ class ActivityPub def process! response = client.get(download_url) - raise FediblockDownloadError unless response.ok? + raise FediblockDownloadError unless response.success? Fediblock.transaction do csv = response.parsed_response diff --git a/app/models/deploy_social_distributed_press.rb b/app/models/deploy_social_distributed_press.rb index c7a103a4..e7f97406 100644 --- a/app/models/deploy_social_distributed_press.rb +++ b/app/models/deploy_social_distributed_press.rb @@ -84,7 +84,7 @@ class DeploySocialDistributedPress < Deploy response = hook_client.put(event: event, hook: webhook) - raise ArgumentError, response.body unless response.ok? + raise ArgumentError, response.body unless response.success? rescue ArgumentError => e ExceptionNotifier.notify_exception(e, data: { site_id: site.name, usuarie_id: rol.usuarie_id }) end From 8d6d215e1af43cd2bc80f24f4aea5a5687f9d286 Mon Sep 17 00:00:00 2001 From: f Date: Tue, 19 Mar 2024 11:21:03 -0300 Subject: [PATCH 109/133] =?UTF-8?q?fix:=20si=20falla=20el=20reporte=20marc?= =?UTF-8?q?arlo=20para=20reenv=C3=ADo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/jobs/activity_pub/remote_flag_job.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/jobs/activity_pub/remote_flag_job.rb b/app/jobs/activity_pub/remote_flag_job.rb index f586a92e..211f46fc 100644 --- a/app/jobs/activity_pub/remote_flag_job.rb +++ b/app/jobs/activity_pub/remote_flag_job.rb @@ -30,6 +30,7 @@ class ActivityPub remote_flag.report! rescue Exception => e ExceptionNotifier.notify_exception(e, data: { remote_flag: remote_flag.id, response: response.parsed_response }) + remote_flag.resend! raise end end From 0ed702992b509cbd6d435474db050228cb88354f Mon Sep 17 00:00:00 2001 From: f Date: Tue, 19 Mar 2024 12:39:41 -0300 Subject: [PATCH 110/133] fix: prevenir objetos multiplicados! parece que la forma en que estabamos creando indices unicos ya no funciona (??) asi que a veces estabamos creando objetos duplicados en threads. de paso actorfetchjob ya no es necesario. closes #15621 closes #15622 closes #15623 closes #15729 closes #15730 closes #15731 --- app/jobs/activity_pub/actor_fetch_job.rb | 26 ---------- app/jobs/activity_pub/fetch_job.rb | 2 +- app/jobs/activity_pub/process_job.rb | 17 +++---- app/models/activity_pub/actor.rb | 2 +- ...240319144735_add_missing_unique_indexes.rb | 48 +++++++++++++++++++ db/structure.sql | 28 +++++++++-- 6 files changed, 82 insertions(+), 41 deletions(-) delete mode 100644 app/jobs/activity_pub/actor_fetch_job.rb create mode 100644 db/migrate/20240319144735_add_missing_unique_indexes.rb diff --git a/app/jobs/activity_pub/actor_fetch_job.rb b/app/jobs/activity_pub/actor_fetch_job.rb deleted file mode 100644 index 598ecd83..00000000 --- a/app/jobs/activity_pub/actor_fetch_job.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -# Obtiene o actualiza el contenido de un objeto, usando las credenciales -# del sitio. -# -# XXX: Esto usa las credenciales del sitio para volver el objeto -# disponible para todo el CMS. Asumimos que el objeto devuelto es el -# mismo para todo el mundo y las credenciales solo son para -# autenticación. -class ActivityPub - class ActorFetchJob < ApplicationJob - self.priority = 50 - - def perform(site:, actor:) - ActivityPub::Actor.transaction do - response = site.social_inbox.dereferencer.get(uri: actor.uri) - - # @todo Fallar cuando la respuesta no funcione? - return unless response.success? - return if response.miss? && actor.content.present? - - actor.object.update(content: FastJsonparser.parse(response.body)) - end - end - end -end diff --git a/app/jobs/activity_pub/fetch_job.rb b/app/jobs/activity_pub/fetch_job.rb index beb585ff..e0033439 100644 --- a/app/jobs/activity_pub/fetch_job.rb +++ b/app/jobs/activity_pub/fetch_job.rb @@ -30,7 +30,7 @@ class ActivityPub current_type = object.type content = FastJsonparser.parse(response.body) - object.update!(content: content, type: ActivityPub::Object.type_from(content).name) + object.lock.update!(content: content, type: ActivityPub::Object.type_from(content).name) object = ::ActivityPub::Object.find(object_id) # Actualiza la mención diff --git a/app/jobs/activity_pub/process_job.rb b/app/jobs/activity_pub/process_job.rb index 9b72be43..4e278797 100644 --- a/app/jobs/activity_pub/process_job.rb +++ b/app/jobs/activity_pub/process_job.rb @@ -63,7 +63,7 @@ class ActivityPub # # @return [ActivityPub::Object] def object - @object ||= ::ActivityPub::Object.find_or_initialize_by(uri: object_uri).tap do |o| + @object ||= ::ActivityPub::Object.lock.find_or_initialize_by(uri: object_uri).tap do |o| o.content = original_object if object_embedded? o.save! @@ -80,8 +80,8 @@ class ActivityPub # # @return [ActivityPub] def activity_pub - @activity_pub ||= site.activity_pubs.find_or_create_by!(site: site, actor: actor, instance: instance, - object_id: object.id, object_type: object.type) + @activity_pub ||= site.activity_pubs.lock.find_or_create_by!(site: site, actor: actor, instance: instance, + object_id: object.id, object_type: object.type) end # Crea la actividad y la vincula con el estado @@ -91,6 +91,7 @@ class ActivityPub @activity ||= ::ActivityPub::Activity .type_from(original_activity) + .lock .find_or_initialize_by(uri: original_activity[:id], activity_pub: activity_pub, actor: actor).tap do |a| a.content = original_activity.dup a.content[:object] = object.uri @@ -103,20 +104,20 @@ class ActivityPub # # @return [Actor] def actor - @actor ||= ::ActivityPub::Actor.find_or_initialize_by(uri: original_activity[:actor]).tap do |a| + @actor ||= ::ActivityPub::Actor.lock.find_or_initialize_by(uri: original_activity[:actor]).tap do |a| unless a.instance - a.instance = ::ActivityPub::Instance.find_or_create_by(hostname: URI.parse(a.uri).hostname) + a.instance = ::ActivityPub::Instance.lock.find_or_create_by(hostname: URI.parse(a.uri).hostname) ::ActivityPub::InstanceFetchJob.perform_later(site: site, instance: a.instance) end - site.instance_moderations.find_or_create_by(instance: a.instance) + site.instance_moderations.lock.find_or_create_by(instance: a.instance) a.save! - site.actor_moderations.find_or_create_by(actor: a) + site.actor_moderations.lock.find_or_create_by(actor: a) - ::ActivityPub::ActorFetchJob.perform_later(site: site, actor: a) + ::ActivityPub::FetchJob.perform_later(site: site, actor: a.object) end end diff --git a/app/models/activity_pub/actor.rb b/app/models/activity_pub/actor.rb index a8cc0d5d..6a284025 100644 --- a/app/models/activity_pub/actor.rb +++ b/app/models/activity_pub/actor.rb @@ -33,7 +33,7 @@ class ActivityPub end def object - @object ||= ActivityPub::Object.find_or_initialize_by(uri: uri) + @object ||= ActivityPub::Object.lock.find_or_create_by(uri: uri) end def content diff --git a/db/migrate/20240319144735_add_missing_unique_indexes.rb b/db/migrate/20240319144735_add_missing_unique_indexes.rb new file mode 100644 index 00000000..7d18c8e8 --- /dev/null +++ b/db/migrate/20240319144735_add_missing_unique_indexes.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +# Parece que la sintaxis que veníamos usando para los índices únicos ya +# no es válida y por eso teníamos objetos duplicados. +class AddMissingUniqueIndexes < ActiveRecord::Migration[6.1] + def up + ActivityPub::Object.group(:uri).count.select { |_, v| v > 1 }.keys.each do |uri| + objects = ActivityPub::Object.where(uri: uri) + deleted_ids = objects[1..].map(&:delete).map(&:id) + + ActivityPub.where(object_id: deleted_ids).update_all(object_id: objects.first.id, updated_at: Time.now) + end + + ActivityPub::Actor.group(:uri).count.select { |_, v| v > 1 }.keys.each do |uri| + objects = ActivityPub::Actor.where(uri: uri) + deleted_ids = objects[1..].map(&:delete).map(&:id) + + ActivityPub.where(actor_id: deleted_ids).update_all(actor_id: objects.first.id, updated_at: Time.now) + ActorModeration.where(actor_id: deleted_ids).update_all(actor_id: objects.first.id, updated_at: Time.now) + ActivityPub::Activity.where(actor_id: deleted_ids).update_all(actor_id: objects.first.id, updated_at: Time.now) + ActivityPub::RemoteFlag.where(actor_id: deleted_ids).update_all(actor_id: objects.first.id, updated_at: Time.now) + end + + ActivityPub::Instance.group(:hostname).count.select { |_, v| v > 1 }.keys.each do |hostname| + objects = ActivityPub::Instance.where(hostname: hostname) + deleted_ids = objects[1..].map(&:delete).map(&:id) + + ActivityPub.where(instance_id: deleted_ids).update_all(instance_id: objects.first.id, updated_at: Time.now) + InstanceModeration.where(instance_id: deleted_ids).update_all(instance_id: objects.first.id, updated_at: Time.now) + ActivityPub::Actor.where(instance_id: deleted_ids).update_all(instance_id: objects.first.id, updated_at: Time.now) + end + + remove_index :activity_pub_instances, :hostname + remove_index :activity_pub_actors, :uri + add_index :activity_pub_instances, :hostname, unique: true + add_index :activity_pub_objects, :uri, unique: true + add_index :activity_pub_actors, :uri, unique: true + end + + def down + remove_index :activity_pub_instances, :hostname, unique: true + remove_index :activity_pub_objects, :uri, unique: true + remove_index :activity_pub_actors, :uri, unique: true + add_index :activity_pub_instances, :hostname + add_index :activity_pub_objects, :uri + add_index :activity_pub_actors, :uri + end +end diff --git a/db/structure.sql b/db/structure.sql index ed58ebec..21cf04d0 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -498,7 +498,8 @@ CREATE TABLE public.activity_pub_actors ( created_at timestamp(6) without time zone NOT NULL, updated_at timestamp(6) without time zone NOT NULL, instance_id uuid NOT NULL, - uri character varying NOT NULL + uri character varying NOT NULL, + mention character varying ); @@ -512,7 +513,7 @@ CREATE TABLE public.activity_pub_fediblocks ( updated_at timestamp(6) without time zone NOT NULL, title character varying NOT NULL, url character varying NOT NULL, - download_url character varying NOT NULL, + download_url character varying, format character varying NOT NULL, hostnames jsonb DEFAULT '[]'::jsonb ); @@ -557,6 +558,7 @@ CREATE TABLE public.activity_pub_remote_flags ( site_id bigint, actor_id uuid, message text, + content jsonb, aasm_state character varying DEFAULT 'waiting'::character varying NOT NULL ); @@ -2138,14 +2140,21 @@ CREATE INDEX index_activity_pub_actors_on_instance_id ON public.activity_pub_act -- Name: index_activity_pub_actors_on_uri; Type: INDEX; Schema: public; Owner: - -- -CREATE INDEX index_activity_pub_actors_on_uri ON public.activity_pub_actors USING btree (uri); +CREATE UNIQUE INDEX index_activity_pub_actors_on_uri ON public.activity_pub_actors USING btree (uri); -- -- Name: index_activity_pub_instances_on_hostname; Type: INDEX; Schema: public; Owner: - -- -CREATE INDEX index_activity_pub_instances_on_hostname ON public.activity_pub_instances USING btree (hostname); +CREATE UNIQUE INDEX index_activity_pub_instances_on_hostname ON public.activity_pub_instances USING btree (hostname); + + +-- +-- Name: index_activity_pub_objects_on_uri; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX index_activity_pub_objects_on_uri ON public.activity_pub_objects USING btree (uri); -- @@ -2701,6 +2710,15 @@ INSERT INTO "schema_migrations" (version) VALUES ('20240305164653'), ('20240305184854'), ('20240307201510'), -('20240307203039'); +('20240307203039'), +('20240313192134'), +('20240313204105'), +('20240314141536'), +('20240314153017'), +('20240314205923'), +('20240316203721'), +('20240318183846'), +('20240319124212'), +('20240319144735'); From 029d4e7c974914e9b17b871362be599440d1bc0b Mon Sep 17 00:00:00 2001 From: f Date: Tue, 19 Mar 2024 13:01:26 -0300 Subject: [PATCH 111/133] fix: object_id closes #15739 closes #15738 closes #15737 --- app/jobs/activity_pub/process_job.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/jobs/activity_pub/process_job.rb b/app/jobs/activity_pub/process_job.rb index 4e278797..f3aeebb4 100644 --- a/app/jobs/activity_pub/process_job.rb +++ b/app/jobs/activity_pub/process_job.rb @@ -117,7 +117,7 @@ class ActivityPub site.actor_moderations.lock.find_or_create_by(actor: a) - ::ActivityPub::FetchJob.perform_later(site: site, actor: a.object) + ::ActivityPub::FetchJob.perform_later(site: site, object_id: a.object.id) end end From d33e7b37a14c4636a2dab745d40831ffc091448a Mon Sep 17 00:00:00 2001 From: f Date: Tue, 19 Mar 2024 13:16:28 -0300 Subject: [PATCH 112/133] fix: lock closes #15740 closes #15741 closes #15742 closes #15743 closes #15744 closes #15745 closes #15746 closes #15747 closes #15748 closes #15749 closes #15750 closes #15751 closes #15752 closes #15753 closes #15754 closes #15755 closes #15756 closes #15757 closes #15758 closes #15759 --- app/jobs/activity_pub/fetch_job.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/jobs/activity_pub/fetch_job.rb b/app/jobs/activity_pub/fetch_job.rb index e0033439..73fc1c9f 100644 --- a/app/jobs/activity_pub/fetch_job.rb +++ b/app/jobs/activity_pub/fetch_job.rb @@ -30,7 +30,9 @@ class ActivityPub current_type = object.type content = FastJsonparser.parse(response.body) - object.lock.update!(content: content, type: ActivityPub::Object.type_from(content).name) + object.with_lock do + object.update!(content: content, type: ActivityPub::Object.type_from(content).name) + end object = ::ActivityPub::Object.find(object_id) # Actualiza la mención From 1ef7996ce90eacea7448293e7f0a29192ee1f1b6 Mon Sep 17 00:00:00 2001 From: f Date: Tue, 19 Mar 2024 13:21:41 -0300 Subject: [PATCH 113/133] fix: agrupar errores para que no nos inunden --- config/environments/production.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/environments/production.rb b/config/environments/production.rb index 5e089ff9..bc7cecd7 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -142,7 +142,7 @@ Rails.application.configure do } config.action_mailer.default_options = { from: ENV.fetch('DEFAULT_FROM', "noreply@sutty.nl") } - config.middleware.use ExceptionNotification::Rack, gitlab: {}, ignore_exceptions: ['DeployJob::DeployAlreadyRunningException'] + config.middleware.use ExceptionNotification::Rack, gitlab: {}, error_grouping: true, ignore_exceptions: ['DeployJob::DeployAlreadyRunningException'] Rails.application.routes.default_url_options[:host] = "panel.#{ENV.fetch('SUTTY', 'sutty.nl')}" Rails.application.routes.default_url_options[:protocol] = 'https' From eafa6fad37cdb15a973f8fc0f3751044c2bab9cc Mon Sep 17 00:00:00 2001 From: f Date: Tue, 19 Mar 2024 17:02:19 -0300 Subject: [PATCH 114/133] fix: cargar el objeto por id y modificarlo closes #15775 closes #15773 closes #15772 closes #15771 closes #15770 closes #15767 closes #15766 closes #15765 closes #15764 --- app/jobs/activity_pub/fetch_job.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/jobs/activity_pub/fetch_job.rb b/app/jobs/activity_pub/fetch_job.rb index 73fc1c9f..b19d5e41 100644 --- a/app/jobs/activity_pub/fetch_job.rb +++ b/app/jobs/activity_pub/fetch_job.rb @@ -30,9 +30,8 @@ class ActivityPub current_type = object.type content = FastJsonparser.parse(response.body) - object.with_lock do - object.update!(content: content, type: ActivityPub::Object.type_from(content).name) - end + # Modificar atómicamente + ::ActivityPub::Object.lock.find(object_id).update!(content: content, type: ActivityPub::Object.type_from(content).name) object = ::ActivityPub::Object.find(object_id) # Actualiza la mención From 28053b2a5676427deed9097717b69ed7bbd7f682 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 20 Mar 2024 16:18:01 -0300 Subject: [PATCH 115/133] =?UTF-8?q?fix:=20las=20cuentas=20reportadas=20tam?= =?UTF-8?q?bi=C3=A9n=20est=C3=A1n=20bloqueadas=20#15649?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/jobs/activity_pub/sync_lists_job.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/jobs/activity_pub/sync_lists_job.rb b/app/jobs/activity_pub/sync_lists_job.rb index cc500bee..de71fe64 100644 --- a/app/jobs/activity_pub/sync_lists_job.rb +++ b/app/jobs/activity_pub/sync_lists_job.rb @@ -18,7 +18,7 @@ class ActivityPub instance_scope = site.instance_moderations.joins(:instance) actor_scope = site.actor_moderations.joins(:actor) - blocklist = wildcardize(instance_scope.blocked.pluck(:hostname)) + actor_scope.blocked.distinct.pluck(:mention).compact + blocklist = wildcardize(instance_scope.blocked.pluck(:hostname)) + actor_scope.blocked.distinct.pluck(:mention).compact + actor_scope.reported.distinct.pluck(:mention).compact allowlist = wildcardize(instance_scope.allowed.pluck(:hostname)) + actor_scope.allowed.distinct.pluck(:mention).compact pauselist = wildcardize(instance_scope.paused.pluck(:hostname)) + actor_scope.paused.distinct.pluck(:mention).compact From 0a1b86c11195af3a5ecaa2002173caa95bd0954e Mon Sep 17 00:00:00 2001 From: f Date: Wed, 20 Mar 2024 17:49:09 -0300 Subject: [PATCH 116/133] fix: se pueden rechazar comentarios luego de aprobarlos #15600 --- app/models/activity_pub.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/activity_pub.rb b/app/models/activity_pub.rb index 913ac67d..335121d2 100644 --- a/app/models/activity_pub.rb +++ b/app/models/activity_pub.rb @@ -128,7 +128,7 @@ class ActivityPub < ApplicationRecord # La actividad fue rechazada event :reject do - transitions from: %i[paused], to: :rejected + transitions from: %i[paused approved], to: :rejected after do ActivityPub::InboxJob.perform_later(site: site, activity: activities.first.uri, action: :reject) From 0578b03ee76f8094c1b7a9fa6ee4fbb491b2503c Mon Sep 17 00:00:00 2001 From: f Date: Wed, 20 Mar 2024 17:50:39 -0300 Subject: [PATCH 117/133] fix: reportar implicar bloquear #15600 --- app/models/activity_pub.rb | 5 +++-- app/models/actor_moderation.rb | 2 +- config/locales/en.yml | 4 ++-- config/locales/es.yml | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/models/activity_pub.rb b/app/models/activity_pub.rb index 335121d2..cd893406 100644 --- a/app/models/activity_pub.rb +++ b/app/models/activity_pub.rb @@ -135,11 +135,12 @@ class ActivityPub < ApplicationRecord end end - # Solo podemos reportarla luego de rechazarla + # Reportarla implica rechazarla event :report do - transitions from: :rejected, to: :reported + transitions from: %i[paused approved rejected], to: :reported after do + ActivityPub::InboxJob.perform_later(site: site, activity: activities.first.uri, action: :reject) ActivityPub::RemoteFlagJob.perform_later(remote_flag: remote_flag) if remote_flag.waiting? end end diff --git a/app/models/actor_moderation.rb b/app/models/actor_moderation.rb index 18149be4..1c3cf83a 100644 --- a/app/models/actor_moderation.rb +++ b/app/models/actor_moderation.rb @@ -43,7 +43,7 @@ class ActorModeration < ApplicationRecord # Al reportar, necesitamos asociar una RemoteFlag para poder # enviarla. event :report do - transitions from: %i[blocked], to: :reported + transitions from: %i[pause allowed blocked], to: :reported, after: :synchronize! after do ActivityPub::RemoteFlagJob.perform_later(remote_flag: remote_flag) if remote_flag.waiting? diff --git a/config/locales/en.yml b/config/locales/en.yml index 35b857e6..643effaf 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -106,7 +106,7 @@ en: text_reject: Reject text_reply: Reply text_report: Report - confirm_report: "Send report to the remote instance?" + confirm_report: "Send report to the remote instance? This action will also reject the comment." instances_btn_box: text_pause: Check case by case text_allow: Allow everything @@ -116,7 +116,7 @@ en: text_allow: Always approve text_block: Block text_report: Report - confirm_report: "Send report to the remote instance?" + confirm_report: "Send report to the remote instance? This action will also block the account." remote_flags: report_message: "Hi! Someone using Sutty CMS reported this account on your instance. We don't have support for customized report messages yet, but we will soon. You can reach us at %{panel_actor_mention}." activity_pubs: diff --git a/config/locales/es.yml b/config/locales/es.yml index 77e611ed..b8bfc524 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -105,7 +105,7 @@ es: text_approve: Aceptar text_reject: Rechazar text_report: Reportar - confirm_report: "¿Enviar el reporte a la instancia remota?" + confirm_report: "¿Enviar el reporte a la instancia remota? Esta acción también rechazará el comentario." instances_btn_box: text_pause: Moderar caso por caso text_allow: Permitir todo @@ -115,7 +115,7 @@ es: text_allow: Aprobar siempre text_block: Bloquear text_report: Reportar - confirm_report: "¿Enviar el reporte a la instancia remota?" + confirm_report: "¿Enviar el reporte a la instancia remota? Esta acción también bloqueará la cuenta." remote_flags: report_message: "¡Hola! Une usuarie de Sutty CMS reportó esta cuenta en tu instancia. Todavía no tenemos soporte para mensajes personalizados. Podés contactarnos en %{panel_actor_mention}." activity_pubs: From e2fdce91f3b2cbfa110559ac2e0b01ef0333ab21 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 20 Mar 2024 17:51:23 -0300 Subject: [PATCH 118/133] fix: agrupar espacialmente acciones #15600 --- app/views/components/_comments_filters.haml | 2 +- app/views/components/_instances_filters.haml | 2 +- app/views/components/_profiles_filters.haml | 2 +- app/views/moderation_queue/_comment.haml | 2 +- config/locales/en.yml | 6 +++--- config/locales/es.yml | 6 +++--- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/views/components/_comments_filters.haml b/app/views/components/_comments_filters.haml index cf8c1aa2..b2870c5a 100644 --- a/app/views/components/_comments_filters.haml +++ b/app/views/components/_comments_filters.haml @@ -3,7 +3,7 @@ - current_state = params[:activity_pub_state]&.to_sym || ActivityPub.states.first -.d-flex.py-2 +.d-flex.flex-row.justify-content-between.py-2 - if ActivityPub.transitionable_events(current_state).present? = render 'components/dropdown', text: t('.text_checked') do = render 'components/comments_checked_submenu', form: form diff --git a/app/views/components/_instances_filters.haml b/app/views/components/_instances_filters.haml index 730184bd..f2296c7b 100644 --- a/app/views/components/_instances_filters.haml +++ b/app/views/components/_instances_filters.haml @@ -3,7 +3,7 @@ - current_state = params[:state]&.to_sym || InstanceModeration.states.first -.d-flex.py-2 +.d-flex.flex-row.justify-content-between.py-2 - if InstanceModeration.transitionable_events(current_state).present? = render 'components/dropdown', text: t('.text_checked') do = render 'components/instances_checked_submenu', form: form, current_state: current_state diff --git a/app/views/components/_profiles_filters.haml b/app/views/components/_profiles_filters.haml index 3f830ec8..c2670944 100644 --- a/app/views/components/_profiles_filters.haml +++ b/app/views/components/_profiles_filters.haml @@ -3,7 +3,7 @@ - current_state = params[:actor_state]&.to_sym || ActorModeration.states.first -.d-flex.py-2 +.d-flex.flex-row.justify-content-between.py-2 - if ActorModeration.transitionable_events(current_state).present? = render 'components/dropdown', text: t('.text_checked') do = render 'components/profiles_checked_submenu', form: form, current_state: current_state diff --git a/app/views/moderation_queue/_comment.haml b/app/views/moderation_queue/_comment.haml index 10f09106..a80bd27c 100644 --- a/app/views/moderation_queue/_comment.haml +++ b/app/views/moderation_queue/_comment.haml @@ -38,7 +38,7 @@ %dd.d-inline %small %a{ href: in_reply_to }= in_reply_to - .content + .content.mb-3 - if summary.present? = render 'layouts/details', summary: summary, summary_class: 'h5' do = sanitize comment['content'] diff --git a/config/locales/en.yml b/config/locales/en.yml index 643effaf..8059b6db 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -63,7 +63,7 @@ en: instances_blocked: Instances blocked instances_filters: text_show: Show - text_checked: With selected + text_checked: With selected... instances_checked_submenu: submenu_pause: Moderate submenu_allow: Allow @@ -74,7 +74,7 @@ en: submenu_blocked: "Blocked (%{count})" comments_filters: text_show: Show - text_checked: With selected + text_checked: With selected... comments_checked_submenu: submenu_pause: Pause submenu_approve: Approve @@ -87,7 +87,7 @@ en: submenu_reported: "Reported (%{count})" profiles_filters: text_show: Show - text_checked: With selected + text_checked: With selected... profiles_checked_submenu: submenu_pause: Pause submenu_allow: Allow diff --git a/config/locales/es.yml b/config/locales/es.yml index b8bfc524..06b933bc 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -63,7 +63,7 @@ es: instances_blocked: Instancias bloqueadas instances_filters: text_show: Ver - text_checked: Con los marcados + text_checked: Con los marcados... instances_checked_submenu: submenu_pause: Moderar caso por caso submenu_allow: Permitir todo @@ -74,7 +74,7 @@ es: submenu_blocked: "Bloqueadas (%{count})" comments_filters: text_show: Ver - text_checked: Con los marcados + text_checked: Con los marcados... comments_checked_submenu: submenu_pause: Pausar submenu_approve: Aprobar @@ -87,7 +87,7 @@ es: submenu_reported: "Reportados (%{count})" profiles_filters: text_show: Ver - text_checked: Con los marcados + text_checked: Con los marcados... profiles_checked_submenu: submenu_pause: Pausar submenu_allow: Aceptar From eda075fd4b889707328c2c62bcc6ac47984944ee Mon Sep 17 00:00:00 2001 From: f Date: Wed, 20 Mar 2024 17:52:31 -0300 Subject: [PATCH 119/133] =?UTF-8?q?fix:=20la=20acci=C3=B3n=20no=20se=20pue?= =?UTF-8?q?de=20hacer=20porque=20es=20el=20estado=20actual=20#15600?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/views/components/_btn_base.haml | 1 - app/views/components/_comments_btn_box.haml | 4 +++- app/views/components/_instances_btn_box.haml | 5 +++-- app/views/components/_profiles_btn_box.haml | 5 +++-- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/views/components/_btn_base.haml b/app/views/components/_btn_base.haml index faa5c85f..fed3254c 100644 --- a/app/views/components/_btn_base.haml +++ b/app/views/components/_btn_base.haml @@ -1,7 +1,6 @@ -# Componente Botón general Moderación - local_assigns[:method] ||= 'patch' -- local_assigns[:class] ||= 'btn-secondary' - local_assigns[:class] = "btn #{local_assigns[:class]}" - local_assigns.delete(:text) diff --git a/app/views/components/_comments_btn_box.haml b/app/views/components/_comments_btn_box.haml index 1993e5cb..ffc773a7 100644 --- a/app/views/components/_comments_btn_box.haml +++ b/app/views/components/_comments_btn_box.haml @@ -4,8 +4,10 @@ .d-flex.flex-row - ActivityPub.events.each do |event| + - possible = activity_pub.public_send(:"may_#{event}?") = render 'components/btn_base', text: t(".text_#{event}"), path: public_send(:"site_activity_pub_#{event}_path", activity_pub_id: activity_pub), - disabled: !activity_pub.public_send(:"may_#{event}?"), + class: ('btn-secondary' if possible), + disabled: !possible, data: local_data[event] diff --git a/app/views/components/_instances_btn_box.haml b/app/views/components/_instances_btn_box.haml index 15c6c040..8c3a5f88 100644 --- a/app/views/components/_instances_btn_box.haml +++ b/app/views/components/_instances_btn_box.haml @@ -2,9 +2,10 @@ - local_data = {} - InstanceModeration.events.each do |event| + - possible = instance_moderation.public_send(:"may_#{event}?") = render 'components/btn_base', path: public_send(:"site_instance_moderation_#{event}_path", instance_moderation_id: instance_moderation), text: t(".text_#{event}"), - class: 'btn btn-secondary', - disabled: !instance_moderation.public_send(:"may_#{event}?"), + class: ('btn-secondary' if possible), + disabled: !possible, data: local_data[event] diff --git a/app/views/components/_profiles_btn_box.haml b/app/views/components/_profiles_btn_box.haml index 488373b9..9414c178 100644 --- a/app/views/components/_profiles_btn_box.haml +++ b/app/views/components/_profiles_btn_box.haml @@ -2,9 +2,10 @@ .d-flex.flex-row - local_data = { report: { confirm: t('.confirm_report') } } - ActorModeration.events.each do |actor_event| + - possible = !actor_moderation.public_send(:"may_#{actor_event}?") = render 'components/btn_base', text: t(".text_#{actor_event}"), path: public_send(:"site_actor_moderation_#{actor_event}_path", actor_moderation_id: actor_moderation), - class: 'btn-secondary', - disabled: !actor_moderation.public_send(:"may_#{actor_event}?"), + class: ('btn-secondary' if possible), + disabled: !possible, data: local_data[actor_event] From 8dae40cb0ebef8c9851b03467962d2bb6bffb724 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 20 Mar 2024 17:53:00 -0300 Subject: [PATCH 120/133] fix: confirmar al rechazar #15600 --- app/views/components/_comments_btn_box.haml | 2 +- config/locales/en.yml | 1 + config/locales/es.yml | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/views/components/_comments_btn_box.haml b/app/views/components/_comments_btn_box.haml index ffc773a7..19ea169c 100644 --- a/app/views/components/_comments_btn_box.haml +++ b/app/views/components/_comments_btn_box.haml @@ -1,6 +1,6 @@ -# Componente Botonera de Comentarios -- local_data = { report: { confirm: t('.confirm_report') } } +- local_data = { reject: { confirm: t('.confirm_reject') }, report: { confirm: t('.confirm_report') } } .d-flex.flex-row - ActivityPub.events.each do |event| diff --git a/config/locales/en.yml b/config/locales/en.yml index 8059b6db..c88f7b26 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -107,6 +107,7 @@ en: text_reply: Reply text_report: Report confirm_report: "Send report to the remote instance? This action will also reject the comment." + confirm_reject: "Reject this comment? Please notice we can't undo this action at this moment." instances_btn_box: text_pause: Check case by case text_allow: Allow everything diff --git a/config/locales/es.yml b/config/locales/es.yml index 06b933bc..695b4e60 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -106,6 +106,7 @@ es: text_reject: Rechazar text_report: Reportar confirm_report: "¿Enviar el reporte a la instancia remota? Esta acción también rechazará el comentario." + confirm_reject: "¿Rechazar este comentario? Tené en cuenta que por el momento no es posible deshacer esta acción." instances_btn_box: text_pause: Moderar caso por caso text_allow: Permitir todo From e05a8aafda806adadea1af6b95c5ed13b8d0565b Mon Sep 17 00:00:00 2001 From: f Date: Thu, 21 Mar 2024 11:14:36 -0300 Subject: [PATCH 121/133] fix: enviar los botones de reporte a la derecha #15600 --- app/views/components/_comments_btn_box.haml | 15 ++++++++------- app/views/components/_profiles_btn_box.haml | 17 +++++++++-------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/app/views/components/_comments_btn_box.haml b/app/views/components/_comments_btn_box.haml index 19ea169c..578f6662 100644 --- a/app/views/components/_comments_btn_box.haml +++ b/app/views/components/_comments_btn_box.haml @@ -1,13 +1,14 @@ -# Componente Botonera de Comentarios -- local_data = { reject: { confirm: t('.confirm_reject') }, report: { confirm: t('.confirm_report') } } +- local = { reject: { data: { confirm: t('.confirm_reject') } }, report: { class: 'ml-auto', data: { confirm: t('.confirm_report') } } } .d-flex.flex-row - ActivityPub.events.each do |event| - possible = activity_pub.public_send(:"may_#{event}?") - = render 'components/btn_base', - text: t(".text_#{event}"), - path: public_send(:"site_activity_pub_#{event}_path", activity_pub_id: activity_pub), - class: ('btn-secondary' if possible), - disabled: !possible, - data: local_data[event] + %div{ class: local.dig(event, :class) } + = render 'components/btn_base', + text: t(".text_#{event}"), + path: public_send(:"site_activity_pub_#{event}_path", activity_pub_id: activity_pub), + class: ('btn-secondary' if possible), + disabled: !possible, + data: local.dig(event, :data) diff --git a/app/views/components/_profiles_btn_box.haml b/app/views/components/_profiles_btn_box.haml index 9414c178..8fc8dd39 100644 --- a/app/views/components/_profiles_btn_box.haml +++ b/app/views/components/_profiles_btn_box.haml @@ -1,11 +1,12 @@ -# Componente Botonera de Moderación de Cuentas (Remote_profile) -.d-flex.flex-row - - local_data = { report: { confirm: t('.confirm_report') } } +.d-flex.flex-row.w-100 + - local = { report: { class: 'ml-auto', data: { confirm: t('.confirm_report') } } } - ActorModeration.events.each do |actor_event| - possible = !actor_moderation.public_send(:"may_#{actor_event}?") - = render 'components/btn_base', - text: t(".text_#{actor_event}"), - path: public_send(:"site_actor_moderation_#{actor_event}_path", actor_moderation_id: actor_moderation), - class: ('btn-secondary' if possible), - disabled: !possible, - data: local_data[actor_event] + %div{ class: local.dig(actor_event, :class) } + = render 'components/btn_base', + text: t(".text_#{actor_event}"), + path: public_send(:"site_actor_moderation_#{actor_event}_path", actor_moderation_id: actor_moderation), + class: ('btn-secondary' if possible), + disabled: !possible, + data: local.dig(actor_event, :data) From 2aa2b9f3bb5e0aecd5f7b256a3248adadbd25087 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 21 Mar 2024 18:12:12 -0300 Subject: [PATCH 122/133] =?UTF-8?q?fix:=20poder=20ir=20a=20la=20cola=20de?= =?UTF-8?q?=20moderaci=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/assets/stylesheets/application.scss | 16 ++++ .../moderation_queue_controller.rb | 3 + app/models/moderation_queue.rb | 3 + app/models/site/social_distributed_press.rb | 23 ++++++ app/policies/moderation_queue_policy.rb | 9 +++ app/views/posts/index.haml | 10 +-- app/views/sites/_moderation_queue.haml | 9 +++ app/views/sites/index.haml | 74 +++++++++---------- 8 files changed, 103 insertions(+), 44 deletions(-) create mode 100644 app/models/moderation_queue.rb create mode 100644 app/policies/moderation_queue_policy.rb create mode 100644 app/views/sites/_moderation_queue.haml diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index cdf97b5b..11c44d90 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -32,6 +32,22 @@ $sizes: ( @import "bootstrap"; @import "editor"; +@each $color, $rgb in $theme-colors { + .#{$color} { + color: var(--#{$color}); + + &:focus { + color: var(--#{$color}); + } + + ::-moz-selection, + ::selection { + background: var(--#{$color}); + color: white; + } + } +} + .editor { .editor-content { figure { diff --git a/app/controllers/moderation_queue_controller.rb b/app/controllers/moderation_queue_controller.rb index 4bd61e38..ef830c41 100644 --- a/app/controllers/moderation_queue_controller.rb +++ b/app/controllers/moderation_queue_controller.rb @@ -11,9 +11,12 @@ class ModerationQueueController < ApplicationController # Cola de moderación viendo todo el sitio def index + authorize ModerationQueue.new(site) breadcrumb site.title, site_posts_path(site) breadcrumb I18n.t('moderation_queue.index.title'), '' + site.moderation_checked! + # @todo cambiar el estado por query @activity_pubs = site.activity_pubs @instance_moderations = rubanok_process(site.instance_moderations, with: InstanceModerationProcessor) diff --git a/app/models/moderation_queue.rb b/app/models/moderation_queue.rb new file mode 100644 index 00000000..31ca3c9b --- /dev/null +++ b/app/models/moderation_queue.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +ModerationQueue = Struct.new(:site) diff --git a/app/models/site/social_distributed_press.rb b/app/models/site/social_distributed_press.rb index 0716a670..8d8d60d4 100644 --- a/app/models/site/social_distributed_press.rb +++ b/app/models/site/social_distributed_press.rb @@ -19,6 +19,29 @@ class Site before_save :generate_private_key_pem!, unless: :private_key_pem? + def moderation_enabled? + deploy_social_inbox.present? + end + + def deploy_social_inbox + @deploy_social_inbox ||= deploys.find_by(type: 'DeploySocialDistributedPress') + end + + def moderation_checked! + deploy_social_inbox.touch + end + + # @return [Bool] + def moderation_needed? + return false unless moderation_enabled? + + last_activity_pub = activity_pubs.order(updated_at: :desc).first&.updated_at + + return false if last_activity_pub.blank? + + last_activity_pub > deploy_social_inbox.updated_at + end + # @return [SocialInbox] def social_inbox @social_inbox ||= SocialInbox.new(site: self) diff --git a/app/policies/moderation_queue_policy.rb b/app/policies/moderation_queue_policy.rb new file mode 100644 index 00000000..75a4c45a --- /dev/null +++ b/app/policies/moderation_queue_policy.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +# Si la cola de moderación está activada y le usuarie tiene permisos de +# usuarie. +ModerationQueuePolicy = Struct.new(:usuarie, :moderation_queue) do + def index? + moderation_queue.site.moderation_enabled? && moderation_queue.site.usuarie?(usuarie) + end +end diff --git a/app/views/posts/index.haml b/app/views/posts/index.haml index 69fb2d8f..6d77b75d 100644 --- a/app/views/posts/index.haml +++ b/app/views/posts/index.haml @@ -1,10 +1,10 @@ %main.row %aside.menu.col-md-3 - = render 'sites/header', site: @site - - = render 'sites/status', site: @site - - = render 'sites/build', site: @site, class: 'btn-block' + .mb-3 + = render 'sites/header', site: @site + = render 'sites/status', site: @site + = render 'sites/build', site: @site, class: 'btn-block' + = render 'sites/moderation_queue', site: @site, class: 'btn-block' %h3= t('posts.new') %table.table.table-sm.mb-3 diff --git a/app/views/sites/_moderation_queue.haml b/app/views/sites/_moderation_queue.haml new file mode 100644 index 00000000..6b39d797 --- /dev/null +++ b/app/views/sites/_moderation_queue.haml @@ -0,0 +1,9 @@ +- if policy(ModerationQueue.new(site)).index? + - moderation_needed = site.moderation_needed? + + - local_assigns[:class] = "btn btn-secondary #{local_assigns[:class]}" + = link_to site_moderation_queue_path(site), class: local_assigns[:class], title: (t('.moderation_needed') if moderation_needed) do + = t('moderation_queue.index.title') + - if moderation_needed + %span.primary ⏺ + %span.sr-only= t('.moderation_needed') diff --git a/app/views/sites/index.haml b/app/views/sites/index.haml index ed87180a..fc8184e1 100644 --- a/app/views/sites/index.haml +++ b/app/views/sites/index.haml @@ -15,43 +15,39 @@ %tbody - @sites.each do |site| - next unless site.jekyll? - - rol = current_usuarie.rol_for_site(site) - -# - TODO: Solo les usuaries cachean porque tenemos que separar - les botones por permisos. - - cache_if (rol.usuarie? && !rol.temporal), [site, I18n.locale] do - %tr - %td - %h2 - - if policy(site).show? - = link_to site.title, site_posts_path(site, locale: site.default_locale) - - else - = site.title - %p.lead= site.description - %br - = link_to t('.visit'), site.url, class: 'btn btn-secondary' - - if rol.temporal - = button_to t('sites.invitations.accept'), - site_usuaries_accept_invitation_path(site), - method: :patch, - title: t('help.sites.invitations.accept'), - class: 'btn btn-secondary' - = button_to t('sites.invitations.reject'), - site_usuaries_reject_invitation_path(site), - method: :patch, - title: t('help.sites.invitations.reject'), - class: 'btn btn-secondary' + %tr + %td + %h2 + - if policy(site).show? + = link_to site.title, site_posts_path(site, locale: site.default_locale) - else - - if policy(site).show? - = render 'layouts/btn_with_tooltip', - tooltip: t('help.sites.edit_posts'), - type: 'success', - link: site_path(site), - text: t('sites.posts') - - if policy(SiteUsuarie.new(site, current_usuarie)).index? - = render 'layouts/btn_with_tooltip', - tooltip: t('usuaries.index.help.self'), - text: t('usuaries.index.title'), - type: 'info', - link: site_usuaries_path(site) - = render 'sites/build', site: site + = site.title + %p.lead= site.description + %br + = link_to t('.visit'), site.url, class: 'btn btn-secondary' + - if current_usuarie.rol_for_site(site).temporal? + = render 'components/btn_base', + text: t('sites.invitations.accept'), + path: site_usuaries_accept_invitation_path(site), + title: t('help.sites.invitations.accept'), + class: 'btn-secondary' + = render 'components/btn_base', + text: t('sites.invitations.reject'), + path: site_usuaries_reject_invitation_path(site), + title: t('help.sites.invitations.reject'), + class: 'btn-secondary' + - else + - if policy(site).show? + = render 'layouts/btn_with_tooltip', + tooltip: t('help.sites.edit_posts'), + type: 'success', + link: site_path(site), + text: t('sites.posts') + = render 'sites/build', site: site + = render 'sites/moderation_queue', site: site + - if policy(SiteUsuarie.new(site, current_usuarie)).index? + = render 'layouts/btn_with_tooltip', + tooltip: t('usuaries.index.help.self'), + text: t('usuaries.index.title'), + type: 'info', + link: site_usuaries_path(site) From ad637189b542a386ba6c09908b0cb59d20a2b91f Mon Sep 17 00:00:00 2001 From: f Date: Thu, 21 Mar 2024 18:25:36 -0300 Subject: [PATCH 123/133] =?UTF-8?q?fixup!=20fix:=20poder=20ir=20a=20la=20c?= =?UTF-8?q?ola=20de=20moderaci=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/locales/en.yml | 2 ++ config/locales/es.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/config/locales/en.yml b/config/locales/en.yml index c88f7b26..f024fa98 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -573,6 +573,8 @@ en: column: "Country" empty: "(couldn't detect country)" sites: + moderation_queue: + moderation_needed: "There are new activities pending revision since the last time you moderated." donations: url: 'https://donaciones.sutty.nl/en/' text: 'Support us' diff --git a/config/locales/es.yml b/config/locales/es.yml index 695b4e60..204a3587 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -577,6 +577,8 @@ es: column: "País" empty: "(no se pudo detectar el país)" sites: + moderation_queue: + moderation_needed: "Hay actividades pendientes de revisión desde la última vez que moderaste." donations: url: 'https://donaciones.sutty.nl/' text: 'Apoyá nuestro trabajo' From 48532633740028bf406d2bd7ca97ab85f9c076bd Mon Sep 17 00:00:00 2001 From: f Date: Fri, 22 Mar 2024 11:53:23 -0300 Subject: [PATCH 124/133] fix: pedir locks antes de guardar closes #15621 closes #15622 closes #15623 closes #15729 closes #15730 closes #15731 closes #15735 closes #15736 --- app/jobs/activity_pub/process_job.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/jobs/activity_pub/process_job.rb b/app/jobs/activity_pub/process_job.rb index f3aeebb4..bd010613 100644 --- a/app/jobs/activity_pub/process_job.rb +++ b/app/jobs/activity_pub/process_job.rb @@ -66,6 +66,7 @@ class ActivityPub @object ||= ::ActivityPub::Object.lock.find_or_initialize_by(uri: object_uri).tap do |o| o.content = original_object if object_embedded? + o.lock! o.save! # XXX: el objeto necesita ser guardado antes de poder @@ -95,6 +96,7 @@ class ActivityPub .find_or_initialize_by(uri: original_activity[:id], activity_pub: activity_pub, actor: actor).tap do |a| a.content = original_activity.dup a.content[:object] = object.uri + a.lock! a.save! end end @@ -113,6 +115,7 @@ class ActivityPub site.instance_moderations.lock.find_or_create_by(instance: a.instance) + a.lock! a.save! site.actor_moderations.lock.find_or_create_by(actor: a) From 0561b022de3eb01d747c4379590904087385f8c2 Mon Sep 17 00:00:00 2001 From: f Date: Fri, 22 Mar 2024 11:57:22 -0300 Subject: [PATCH 125/133] fix: no fallar si no hay registros sobre los que actuar #15725 --- app/controllers/activity_pubs_controller.rb | 6 ++++-- app/controllers/actor_moderations_controller.rb | 6 ++++-- app/controllers/instance_moderations_controller.rb | 6 ++++-- app/policies/instance_moderation_policy.rb | 2 +- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/app/controllers/activity_pubs_controller.rb b/app/controllers/activity_pubs_controller.rb index d5041a84..428d5cb1 100644 --- a/app/controllers/activity_pubs_controller.rb +++ b/app/controllers/activity_pubs_controller.rb @@ -29,16 +29,18 @@ class ActivityPubsController < ApplicationController end def action_on_several + redirect_to_moderation_queue! + activity_pubs = site.activity_pubs.where(id: params[:activity_pub]) + return if activity_pubs.count.zero? + authorize activity_pubs action = params[:activity_pub_action].to_sym method = :"#{action}_all!" may = :"may_#{action}?" - redirect_to_moderation_queue! - return unless ActivityPub.events.include? action # Crear una sola remote flag por autore diff --git a/app/controllers/actor_moderations_controller.rb b/app/controllers/actor_moderations_controller.rb index 739b1f46..04d2603b 100644 --- a/app/controllers/actor_moderations_controller.rb +++ b/app/controllers/actor_moderations_controller.rb @@ -48,16 +48,18 @@ class ActorModerationsController < ApplicationController end def action_on_several + redirect_to_moderation_queue! + actor_moderations = site.actor_moderations.where(id: params[:actor_moderation]) + return if actor_moderations.count.zero? + authorize actor_moderations action = params[:actor_moderation_action].to_sym method = :"#{action}_all!" may = :"may_#{action}?" - redirect_to_moderation_queue! - return unless ActorModeration.events.include? action ActorModeration.transaction do diff --git a/app/controllers/instance_moderations_controller.rb b/app/controllers/instance_moderations_controller.rb index 13d7f428..de990eb1 100644 --- a/app/controllers/instance_moderations_controller.rb +++ b/app/controllers/instance_moderations_controller.rb @@ -22,15 +22,17 @@ class InstanceModerationsController < ApplicationController end def action_on_several + redirect_to_moderation_queue! + instance_moderations = site.instance_moderations.where(id: params[:instance_moderation]) + return if instance_moderations.count.zero? + authorize instance_moderations action = params[:instance_moderation_action].to_sym method = :"#{action}_all!" - redirect_to_moderation_queue! - return unless InstanceModeration.events.include? action InstanceModeration.transaction do diff --git a/app/policies/instance_moderation_policy.rb b/app/policies/instance_moderation_policy.rb index 13ebfeca..c54eb4b8 100644 --- a/app/policies/instance_moderation_policy.rb +++ b/app/policies/instance_moderation_policy.rb @@ -11,6 +11,6 @@ InstanceModerationPolicy = Struct.new(:usuarie, :instance_moderation) do # En este paso tenemos varias instancias por moderar pero todas son # del mismo sitio. def action_on_several? - instance_moderation.first.site.usuarie? usuarie + instance_moderation.first.presence && instance_moderation.first.site.usuarie? usuarie end end From cc654be121986126642308d68c0b9fdbb6cdb4a3 Mon Sep 17 00:00:00 2001 From: f Date: Fri, 22 Mar 2024 12:01:08 -0300 Subject: [PATCH 126/133] fixup! fix: no fallar si no hay registros sobre los que actuar #15725 --- app/policies/instance_moderation_policy.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/policies/instance_moderation_policy.rb b/app/policies/instance_moderation_policy.rb index c54eb4b8..c6a229d3 100644 --- a/app/policies/instance_moderation_policy.rb +++ b/app/policies/instance_moderation_policy.rb @@ -11,6 +11,6 @@ InstanceModerationPolicy = Struct.new(:usuarie, :instance_moderation) do # En este paso tenemos varias instancias por moderar pero todas son # del mismo sitio. def action_on_several? - instance_moderation.first.presence && instance_moderation.first.site.usuarie? usuarie + instance_moderation.first.presence && instance_moderation.first.site.usuarie?(usuarie) end end From b6fdc8a5801ebacee50eb0b522a374ca4ac3aa52 Mon Sep 17 00:00:00 2001 From: f Date: Fri, 22 Mar 2024 12:38:54 -0300 Subject: [PATCH 127/133] =?UTF-8?q?fix:=20permitir=20hostnames=20num=C3=A9?= =?UTF-8?q?ricos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes #15789 --- app/models/activity_pub/instance.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/activity_pub/instance.rb b/app/models/activity_pub/instance.rb index 749d98ac..cd14ef23 100644 --- a/app/models/activity_pub/instance.rb +++ b/app/models/activity_pub/instance.rb @@ -9,7 +9,7 @@ class ActivityPub include AASM validates :aasm_state, presence: true, inclusion: { in: %w[paused allowed blocked] } - validates :hostname, uniqueness: true, hostname: true + validates :hostname, uniqueness: true, hostname: { allow_numeric_hostname: true } has_many :activity_pubs has_many :actors From 5db263ef6c65450ae997e26a9ba9951f7ea859aa Mon Sep 17 00:00:00 2001 From: f Date: Fri, 22 Mar 2024 12:54:27 -0300 Subject: [PATCH 128/133] fix: recordar que hay que volver a correr la tarea manualmente #15789 --- app/jobs/activity_pub/process_job.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/jobs/activity_pub/process_job.rb b/app/jobs/activity_pub/process_job.rb index bd010613..94799735 100644 --- a/app/jobs/activity_pub/process_job.rb +++ b/app/jobs/activity_pub/process_job.rb @@ -28,7 +28,7 @@ class ActivityPub # Al generar una excepción, en lugar de seguir intentando, enviamos # el reporte. rescue Exception => e - ExceptionNotifier.notify_exception(e, data: { site: site.name, activity: original_activity }) + ExceptionNotifier.notify_exception(e, data: { site: site.name, body: body, initial_state: initial_state, activity: original_activity, message: 'Esta acción se canceló automáticamente, para regenerarla, volver a correr el proceso con los mismos parámetros.' }) end private From dad5f5f00ce99d9b724853b6eaaa1ea0a0be70b8 Mon Sep 17 00:00:00 2001 From: f Date: Fri, 22 Mar 2024 13:06:58 -0300 Subject: [PATCH 129/133] =?UTF-8?q?fix:=20solo=20bloquear=20cuando=20ya=20?= =?UTF-8?q?est=C3=A1=20en=20la=20base=20de=20datos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes #15791 --- app/jobs/activity_pub/process_job.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/jobs/activity_pub/process_job.rb b/app/jobs/activity_pub/process_job.rb index 94799735..9b7c30fc 100644 --- a/app/jobs/activity_pub/process_job.rb +++ b/app/jobs/activity_pub/process_job.rb @@ -66,7 +66,7 @@ class ActivityPub @object ||= ::ActivityPub::Object.lock.find_or_initialize_by(uri: object_uri).tap do |o| o.content = original_object if object_embedded? - o.lock! + o.lock! if o.persisted? o.save! # XXX: el objeto necesita ser guardado antes de poder @@ -96,7 +96,7 @@ class ActivityPub .find_or_initialize_by(uri: original_activity[:id], activity_pub: activity_pub, actor: actor).tap do |a| a.content = original_activity.dup a.content[:object] = object.uri - a.lock! + a.lock! if o.persisted? a.save! end end @@ -115,7 +115,7 @@ class ActivityPub site.instance_moderations.lock.find_or_create_by(instance: a.instance) - a.lock! + a.lock! if o.persisted? a.save! site.actor_moderations.lock.find_or_create_by(actor: a) From 071f20762c3ed2d435ed5351d5b9b5cd937865e5 Mon Sep 17 00:00:00 2001 From: f Date: Fri, 22 Mar 2024 13:09:13 -0300 Subject: [PATCH 130/133] fix: bloquear antes de empezar a modificar closes #15791 --- app/jobs/activity_pub/process_job.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/jobs/activity_pub/process_job.rb b/app/jobs/activity_pub/process_job.rb index 9b7c30fc..a79abf5d 100644 --- a/app/jobs/activity_pub/process_job.rb +++ b/app/jobs/activity_pub/process_job.rb @@ -64,9 +64,9 @@ class ActivityPub # @return [ActivityPub::Object] def object @object ||= ::ActivityPub::Object.lock.find_or_initialize_by(uri: object_uri).tap do |o| + o.lock! if o.persisted? o.content = original_object if object_embedded? - o.lock! if o.persisted? o.save! # XXX: el objeto necesita ser guardado antes de poder @@ -94,9 +94,9 @@ class ActivityPub .type_from(original_activity) .lock .find_or_initialize_by(uri: original_activity[:id], activity_pub: activity_pub, actor: actor).tap do |a| + a.lock! if o.persisted? a.content = original_activity.dup a.content[:object] = object.uri - a.lock! if o.persisted? a.save! end end @@ -107,6 +107,8 @@ class ActivityPub # @return [Actor] def actor @actor ||= ::ActivityPub::Actor.lock.find_or_initialize_by(uri: original_activity[:actor]).tap do |a| + a.lock! if o.persisted? + unless a.instance a.instance = ::ActivityPub::Instance.lock.find_or_create_by(hostname: URI.parse(a.uri).hostname) @@ -114,8 +116,6 @@ class ActivityPub end site.instance_moderations.lock.find_or_create_by(instance: a.instance) - - a.lock! if o.persisted? a.save! site.actor_moderations.lock.find_or_create_by(actor: a) From dbf15397e53de6ce46f148a1be61ef98ab6fefb8 Mon Sep 17 00:00:00 2001 From: f Date: Fri, 22 Mar 2024 15:44:28 -0300 Subject: [PATCH 131/133] fix: mostrar los desplegables como accionables #15600 --- app/views/components/_dropdown.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/components/_dropdown.haml b/app/views/components/_dropdown.haml index 54ddcffb..6f34950b 100644 --- a/app/views/components/_dropdown.haml +++ b/app/views/components/_dropdown.haml @@ -11,7 +11,7 @@ controller: 'dropdown' } } - %button.btn.dropdown-toggle{ + %button.btn.btn-outline-secondary.dropdown-toggle{ type: 'button', class: button_classes, data: { From 07fd2cff930d26e33e8e3ac3fb3307554a3166b0 Mon Sep 17 00:00:00 2001 From: f Date: Fri, 22 Mar 2024 17:31:53 -0300 Subject: [PATCH 132/133] fix: usar locks en moderaciones de instancia closes #15788 --- app/jobs/activity_pub/instance_moderation_job.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/jobs/activity_pub/instance_moderation_job.rb b/app/jobs/activity_pub/instance_moderation_job.rb index 00100abf..9da0627f 100644 --- a/app/jobs/activity_pub/instance_moderation_job.rb +++ b/app/jobs/activity_pub/instance_moderation_job.rb @@ -9,7 +9,7 @@ class ActivityPub def perform(site:, hostnames:, perform_remotely: true) # Crear las instancias que no existan todavía hostnames.each do |hostname| - ActivityPub::Instance.find_or_create_by(hostname: hostname) + ActivityPub::Instance.lock.find_or_create_by(hostname: hostname) end instances = ActivityPub::Instance.where(hostname: hostnames) @@ -20,7 +20,7 @@ class ActivityPub instances.find_each do |instance| # Esto bloquea cada una individualmente en la Social Inbox, # idealmente son pocas instancias las que aparecen. - site.instance_moderations.find_or_create_by(instance: instance) + site.instance_moderations.lock.find_or_create_by(instance: instance) end scope = site.instance_moderations.where(instance_id: instances.ids) From 366494b61535358b6fecaa13482ff3dde10358af Mon Sep 17 00:00:00 2001 From: f Date: Fri, 22 Mar 2024 18:28:34 -0300 Subject: [PATCH 133/133] fix: typo closes #15792 closes #15793 closes #15794 closes #15795 closes #15796 closes #15797 closes #15798 closes #15799 closes #15800 closes #15801 closes #15802 closes #15803 closes #15804 closes #15805 closes #15806 closes #15807 closes #15808 closes #15809 closes #15810 closes #15811 closes #15812 closes #15813 closes #15814 closes #15815 closes #15816 closes #15817 closes #15818 --- app/jobs/activity_pub/process_job.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/jobs/activity_pub/process_job.rb b/app/jobs/activity_pub/process_job.rb index a79abf5d..af686dbd 100644 --- a/app/jobs/activity_pub/process_job.rb +++ b/app/jobs/activity_pub/process_job.rb @@ -94,7 +94,7 @@ class ActivityPub .type_from(original_activity) .lock .find_or_initialize_by(uri: original_activity[:id], activity_pub: activity_pub, actor: actor).tap do |a| - a.lock! if o.persisted? + a.lock! if a.persisted? a.content = original_activity.dup a.content[:object] = object.uri a.save! @@ -107,7 +107,7 @@ class ActivityPub # @return [Actor] def actor @actor ||= ::ActivityPub::Actor.lock.find_or_initialize_by(uri: original_activity[:actor]).tap do |a| - a.lock! if o.persisted? + a.lock! if a.persisted? unless a.instance a.instance = ::ActivityPub::Instance.lock.find_or_create_by(hostname: URI.parse(a.uri).hostname)