diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bb674844..c72a632d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,3 +1,6 @@ +stages: +- "test" +- "deploy" .apk-add: &apk-add - "apk add go-task diffutils gitlab_ci_log_section" .disable-hainish: &disable-hainish @@ -6,9 +9,15 @@ - paths: - "vendor/ruby" - ".bundle" + key: + files: + - "Gemfile.lock" .cache-node: &cache-node - paths: - "node_modules" + key: + files: + - "yarn.lock" .cache-task: &cache-task - paths: - ".task" @@ -18,11 +27,25 @@ variables: LC_ALL: "C.UTF-8" HAINISH: "" cache: +push: + stage: "test" + only: + - "rails" + except: + - "schedules" + before_script: + - "git config --global user.email \"${GIT_USER_EMAIL:-$GITLAB_USER_EMAIL}\"" + - "git config --global user.name \"${GIT_USER_NAME:-$GITLAB_USER_NAME}\"" + - "git remote set-url --push origin \"https://GITLAB_CI_PUSH_TOKEN:${GITLAB_CI_PUSH_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git\"" + script: + - "git commit --allow-empty -m \"ci: test [skip ci]\"" + - "git push -o ci.skip origin HEAD:${CI_COMMIT_BRANCH}" assets: stage: "deploy" only: - "rails" - - "17.3.alpine.panel.sutty.nl" + - "production.panel.sutty.nl" + - "panel.sutty.nl" except: - "schedules" cache: @@ -30,14 +53,14 @@ assets: - *cache-node - *cache-task before_script: + - *apk-add - "gitlab_ci_log_section --name git --header=\"Configuring git\"" - "git config --global user.email \"${GIT_USER_EMAIL:-$GITLAB_USER_EMAIL}\"" - "git config --global user.name \"${GIT_USER_NAME:-$GITLAB_USER_NAME}\"" - - "git remote set-url --push origin \"https://${GITLAB_USERNAME}:${GITLAB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git\"" + - "git remote set-url --push origin \"https://GITLAB_CI_PUSH_TOKEN:${GITLAB_CI_PUSH_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git\"" - "gitlab_ci_log_section --name git --end" - "gitlab_ci_log_section --name apk --header=\"Installing dependencies\"" - "apk add brotli" - - *apk-add - *disable-hainish - "gitlab_ci_log_section --name apk --end" script: @@ -45,7 +68,7 @@ assets: - "go-task assets" after_script: - "git add public && git commit -m \"ci: assets [skip ci]\"" - - "git push -o ci.skip" + - "git push -o ci.skip origin HEAD:${CI_COMMIT_BRANCH}" gem-audit: stage: "test" only: diff --git a/Gemfile b/Gemfile index 47504f8f..59823dd0 100644 --- a/Gemfile +++ b/Gemfile @@ -80,6 +80,7 @@ gem 'yaml_db', git: 'https://0xacab.org/sutty/yaml_db.git' gem 'kaminari' gem 'device_detector' gem 'htmlbeautifier' +gem 'dry-schema' gem 'rubanok' gem 'after_commit_everywhere', '~> 1.0' diff --git a/Gemfile.lock b/Gemfile.lock index e20d74be..32547ac7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -646,6 +646,7 @@ DEPENDENCIES distributed-press-api-client (~> 0.4.1) dotenv-rails down + dry-schema ed25519 email_address! exception_notification diff --git a/Taskfile.yaml b/Taskfile.yaml index 57fb0238..796ab721 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -185,9 +185,13 @@ tasks: - "test -f ../hain/usr/bin/bundler-audit" rubocop: desc: "Ruby linting" + deps: + - "gems" cmds: - "./bin/modified_files | ./bin/with_extension rb | xargs -r {{.HAINISH}} bundle exec rubocop {{.CLI_ARGS}}" haml-lint: desc: "HAML linting" + deps: + - "gems" cmds: - "./bin/modified_files | ./bin/with_extension haml | xargs -r {{.HAINISH}} bundle exec haml-lint {{.CLI_ARGS}}" diff --git a/app/controllers/api/v1/contact_controller.rb b/app/controllers/api/v1/contact_controller.rb index d949dc30..c340097f 100644 --- a/app/controllers/api/v1/contact_controller.rb +++ b/app/controllers/api/v1/contact_controller.rb @@ -18,7 +18,7 @@ module Api # Si todo salió bien, enviar los correos y redirigir al sitio. # El sitio nos dice a dónde tenemos que ir. - ContactJob.perform_later site.id, + ContactJob.perform_later site, params[:form], contact_params.to_h.symbolize_keys, params[:redirect] diff --git a/app/controllers/api/v1/notices_controller.rb b/app/controllers/api/v1/notices_controller.rb index 8f384f1a..01bec517 100644 --- a/app/controllers/api/v1/notices_controller.rb +++ b/app/controllers/api/v1/notices_controller.rb @@ -11,7 +11,7 @@ module Api # respondemos con lo mismo. def create if (site&.airbrake_valid? airbrake_token) && !detected_device.bot? - BacktraceJob.perform_later site_id: params[:site_id], + BacktraceJob.perform_later site: site, params: airbrake_params.to_h end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 5b78b56a..a96d1ec0 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -14,7 +14,7 @@ class ApplicationController < ActionController::Base after_action :store_location! before_action do - Rack::MiniProfiler.authorize_request if current_usuarie&.email&.ends_with?('@' + ENV.fetch('SUTTY', 'sutty.nl')) + Rack::MiniProfiler.authorize_request if current_usuarie&.email&.ends_with?("@#{ENV.fetch('SUTTY', 'sutty.nl')}") end # No tenemos índice de sutty, vamos directamente a ver el listado de @@ -24,7 +24,7 @@ class ApplicationController < ActionController::Base end private - + def notify_unconfirmed_email return unless current_usuarie return if current_usuarie.confirmed? @@ -58,9 +58,7 @@ class ApplicationController < ActionController::Base def current_locale locale = params[:change_locale_to] - if locale.present? && I18n.locale_available?(locale) - session[:locale] = params[:change_locale_to] - end + session[:locale] = params[:change_locale_to] if locale.present? && I18n.locale_available?(locale) session[:locale] || current_usuarie&.lang || I18n.locale end @@ -121,5 +119,4 @@ class ApplicationController < ActionController::Base session[:usuarie_return_to] = request.fullpath end - end diff --git a/app/controllers/env_controller.rb b/app/controllers/env_controller.rb index de61c704..500cdee4 100644 --- a/app/controllers/env_controller.rb +++ b/app/controllers/env_controller.rb @@ -4,7 +4,7 @@ class EnvController < ActionController::Base skip_before_action :verify_authenticity_token def index - @site = Site.find_by_name('panel') + @site = Site.find_by_name('panel') || Site.first stale? @site if @site end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 2b64ead1..684c5a7f 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -13,7 +13,7 @@ module ApplicationHelper root = names.shift names.each do |n| - root += '[' + n.to_s + ']' + root += "[#{n}]" end [root, name] @@ -41,7 +41,7 @@ module ApplicationHelper def plain_field_name_for(*names) root, name = field_name_for(*names) - root + '[' + name.to_s + ']' + "#{root}[#{name}]" end def distance_of_time_in_words_if_more_than_a_minute(seconds) diff --git a/app/jobs/activity_pub/fetch_job.rb b/app/jobs/activity_pub/fetch_job.rb index b19d5e41..07190c35 100644 --- a/app/jobs/activity_pub/fetch_job.rb +++ b/app/jobs/activity_pub/fetch_job.rb @@ -11,14 +11,33 @@ class ActivityPub class FetchJob < ApplicationJob self.priority = 50 + attr_reader :object, :response + + # Notificar errores de JSON con el contenido, tomar los errores de + # validación y conexión como errores temporales y notificar todo lo + # demás sin reintentar. + # + # @param error [Exception] + # @return [Bool] + discard_on(FastJsonparser::ParseError) do |error| + ExceptionNotifier.notify_exception(error, data: { site: site.name, object: object.uri, body: response.body }) + end + + retry_on ActiveRecord::RecordInvalid + retry_on SocketError, wait: ApplicationJob.random_wait + retry_on SystemCallError, wait: ApplicationJob.random_wait + retry_on Net::OpenTimeout, wait: ApplicationJob.random_wait + retry_on OpenSSL::OpenSSLError, wait: ApplicationJob.random_wait + def perform(site:, object_id:) ActivityPub::Object.transaction do - object = ::ActivityPub::Object.find(object_id) + @site = site + @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) + @response = site.social_inbox.dereferencer.get(uri: object.uri) # @todo Fallar cuando la respuesta no funcione? # @todo Eliminar en 410 Gone @@ -31,7 +50,8 @@ class ActivityPub content = FastJsonparser.parse(response.body) # Modificar atómicamente - ::ActivityPub::Object.lock.find(object_id).update!(content: content, type: ActivityPub::Object.type_from(content).name) + ::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 @@ -39,8 +59,6 @@ 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, object: object.uri, body: response.body }) end end end diff --git a/app/jobs/activity_pub/process_job.rb b/app/jobs/activity_pub/process_job.rb index d94c31e2..69c83e33 100644 --- a/app/jobs/activity_pub/process_job.rb +++ b/app/jobs/activity_pub/process_job.rb @@ -3,7 +3,9 @@ class ActivityPub # Procesar las actividades a medida que llegan class ProcessJob < ApplicationJob - attr_reader :body, :initial_state + attr_reader :body + + retry_on ActiveRecord::RecordInvalid # Procesa la actividad en segundo plano # @@ -12,7 +14,6 @@ class ActivityPub def perform(site:, body:, initial_state: :paused) @site = site @body = body - @initial_state = initial_state ActiveRecord::Base.connection_pool.with_connection do ::ActivityPub.transaction do @@ -28,24 +29,6 @@ class ActivityPub end end - # Al generar una excepción, en lugar de seguir intentando, enviamos - # el reporte. - def handle_error(error) - case error - when ActiveRecord::RecordInvalid then retry_in(ApplicationJob.random_wait) - else - ExceptionNotifier.notify_exception( - error, - 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 - end - private # Si el objeto ya viene incorporado en la actividad o lo tenemos diff --git a/app/jobs/activity_pub/sync_lists_job.rb b/app/jobs/activity_pub/sync_lists_job.rb index de71fe64..e37e15be 100644 --- a/app/jobs/activity_pub/sync_lists_job.rb +++ b/app/jobs/activity_pub/sync_lists_job.rb @@ -43,7 +43,9 @@ class ActivityPub # 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 }) + ExceptionNotifier.notify_exception(e, + data: { site: site.name, logs: logs, blocklist: blocklist, + allowlist: allowlist, pauselist: pauselist }) raise end diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb index dc6d0478..ee4e3b2c 100644 --- a/app/jobs/application_job.rb +++ b/app/jobs/application_job.rb @@ -8,16 +8,17 @@ class ApplicationJob < ActiveJob::Base # superpongan tareas # # @return [Array] - RANDOM_WAIT = [3, 5, 7, 11, 13] + RANDOM_WAIT = [3, 5, 7, 11, 13].freeze # @return [ActiveSupport::Duration] def self.random_wait RANDOM_WAIT.sample.seconds end - private + attr_reader :site - def site - @site ||= Site.find @params[:site_id] + # Si falla por cualquier cosa informar y descartar + discard_on(Exception) do |job, error| + ExceptionNotifier.notify_exception(error, data: { job: job }) end end diff --git a/app/jobs/backtrace_job.rb b/app/jobs/backtrace_job.rb index 97e6007b..4ef5287c 100644 --- a/app/jobs/backtrace_job.rb +++ b/app/jobs/backtrace_job.rb @@ -6,10 +6,10 @@ class BacktraceJob < ApplicationJob EMPTY_SOURCEMAP = { 'mappings' => '' }.freeze - attr_reader :params, :site_id + attr_reader :params - def perform(site_id:, params:) - @site_id = site_id + def perform(site:, params:) + @site = site @params = params unless sources.empty? @@ -44,10 +44,6 @@ class BacktraceJob < ApplicationJob private - def site - @site ||= Site.find_by_id(site_id) - end - # Obtiene todos los archivos del backtrace solo si los puede descargar # desde fuentes seguras. # @@ -59,9 +55,7 @@ class BacktraceJob < ApplicationJob x['backtrace'] end.flatten.map do |x| x['file'].split('@').last - end.uniq.select do |x| - %r{\Ahttps://} =~ x - end + end.uniq.grep(%r{\Ahttps://}) end # Descarga y devuelve los datos de un archivo diff --git a/app/jobs/contact_job.rb b/app/jobs/contact_job.rb index c15d7eee..d4c2677f 100644 --- a/app/jobs/contact_job.rb +++ b/app/jobs/contact_job.rb @@ -5,10 +5,8 @@ class ContactJob < ApplicationJob # @param [Integer] # @param [String] # @param [Hash] - def perform(site_id, form_name, form, origin = nil) - # Retrocompabilidad al actualizar a 2.7.1 - # @see ApplicationJob#site - @params = { site_id: site_id } + def perform(site, form_name, form, origin = nil) + @site = site # Sanitizar los valores form.each_key do |key| @@ -23,7 +21,7 @@ class ContactJob < ApplicationJob usuaries.each_slice(10) do |u| ContactMailer.with(form_name: form_name, form: form, - site_id: site_id, + site: site, usuaries_emails: u, origin: origin) .notify_usuaries.deliver_now diff --git a/app/jobs/deploy_job.rb b/app/jobs/deploy_job.rb index 1476315d..b91f4d0d 100644 --- a/app/jobs/deploy_job.rb +++ b/app/jobs/deploy_job.rb @@ -11,44 +11,36 @@ class DeployJob < ApplicationJob # Lanzar lo antes posible self.priority = 10 - def handle_error(error) - case error - when DeployAlreadyRunningException then retry_in 1.minute - when DeployTimedOutException then expire - else super - end - end + retry_on DeployAlreadyRunningException, wait: 1.minute + discard_on DeployTimedOutException # rubocop:disable Metrics/MethodLength def perform(site, notify: true, time: Time.now, output: false) - @output = output + @site = site ActiveRecord::Base.connection_pool.with_connection do - @site = Site.find(site) - # Si ya hay una tarea corriendo, aplazar esta. Si estuvo # esperando más de 10 minutos, recuperar el estado anterior. # # Como el trabajo actual se aplaza al siguiente, arrastrar la # hora original para poder ir haciendo timeouts. - if @site.building? + if site.building? notify = false - if 10.minutes.ago >= time - raise DeployTimedOutException, - "#{@site.name} la tarea estuvo más de 10 minutos esperando, volviendo al estado original" - else - raise DeployAlreadyRunningException - end + raise DeployAlreadyRunningException unless 10.minutes.ago >= time + + raise DeployTimedOutException, + "#{site.name} la tarea estuvo más de 10 minutos esperando, volviendo al estado original" + end @deployed = {} - @site.update status: 'building' - @site.deployment_list.each do |d| + site.update status: 'building' + site.deployment_list.each do |d| begin raise DeployException, 'Una dependencia falló' if failed_dependencies? d - status = d.deploy(output: @output) + status = d.deploy(output: output) seconds = d.build_stats.last.try(:seconds) || 0 size = d.size urls = d.urls.map do |url| @@ -57,9 +49,7 @@ class DeployJob < ApplicationJob nil end.compact - if d == @site.deployment_list.last && !status - raise DeployException, 'Falló la compilación' - end + raise DeployException, 'Falló la compilación' if d == site.deployment_list.last && !status rescue StandardError => e status = false seconds ||= 0 @@ -78,9 +68,9 @@ class DeployJob < ApplicationJob } end - return unless @output + return unless output - puts (Terminal::Table.new do |t| + puts(Terminal::Table.new do |t| t << (%w[type] + @deployed.values.first.keys) t.add_separator @deployed.each do |type, row| @@ -90,12 +80,12 @@ class DeployJob < ApplicationJob rescue DeployTimedOutException => e notify_exception e ensure - if @site.present? - @site.update status: 'waiting' + if site.present? + site.update status: 'waiting' notify_usuaries if notify - puts "\a" if @output + puts "\a" if output end end end @@ -125,7 +115,7 @@ class DeployJob < ApplicationJob # @param :deploy [Deploy] def notify_exception(exception, deploy = nil) data = { - site: @site.id, + site: site.name, deploy: deploy&.type, log: deploy&.build_stats&.last&.log, failed_dependencies: (failed_dependencies(deploy) if deploy) @@ -135,8 +125,10 @@ class DeployJob < ApplicationJob end def notify_usuaries - @site.roles.where(rol: 'usuarie', temporal: false).pluck(:usuarie_id).each do |usuarie| - DeployMailer.with(usuarie: usuarie, site: @site.id) + usuarie_ids = site.roles.where(rol: 'usuarie', temporal: false).pluck(:usuarie_id) + + Usuarie.where(id: usuarie_ids).find_each do |usuarie| + DeployMailer.with(usuarie: usuarie, site: site) .deployed(@deployed) .deliver_now end diff --git a/app/jobs/git_pull_job.rb b/app/jobs/git_pull_job.rb index 16e2fe5b..30431495 100644 --- a/app/jobs/git_pull_job.rb +++ b/app/jobs/git_pull_job.rb @@ -7,6 +7,8 @@ class GitPullJob < ApplicationJob # @param :message [String] # @return [nil] def perform(site, usuarie, message) + @site = site + return unless site.repository.origin site.repository.fetch diff --git a/app/jobs/git_push_job.rb b/app/jobs/git_push_job.rb index 3c62bee2..4df9f5aa 100644 --- a/app/jobs/git_push_job.rb +++ b/app/jobs/git_push_job.rb @@ -6,6 +6,8 @@ class GitPushJob < ApplicationJob # @param :site [Site] # @return [nil] def perform(site) - site.repository.push if site.repository.origin + @site = site + + site.repository.push if site.repository.origin end -end \ No newline at end of file +end diff --git a/app/jobs/maintenance_job.rb b/app/jobs/maintenance_job.rb index c7a962f9..02f29a77 100644 --- a/app/jobs/maintenance_job.rb +++ b/app/jobs/maintenance_job.rb @@ -15,8 +15,7 @@ # Lo mismo para salir de mantenimiento, agregando el atributo # are_we_back: true al crear el Maintenance. class MaintenanceJob < ApplicationJob - def perform(maintenance_id:) - maintenance = Maintenance.find(maintenance_id) + def perform(maintenance:) # Decidir cuál vamos a enviar según el estado de Maintenance mailer = maintenance.are_we_back ? :were_back : :notice diff --git a/app/jobs/periodic_job.rb b/app/jobs/periodic_job.rb index 2f60a2b3..f66434c9 100644 --- a/app/jobs/periodic_job.rb +++ b/app/jobs/periodic_job.rb @@ -6,9 +6,6 @@ class PeriodicJob < ApplicationJob STARTING_INTERVAL = Stat::INTERVALS.first - # Tener el sitio a mano - attr_reader :site - # Descartar y notificar si pasó algo más. # # XXX: En realidad deberíamos seguir reintentando? diff --git a/app/jobs/stat_collection_job.rb b/app/jobs/stat_collection_job.rb index e402e3b5..02752901 100644 --- a/app/jobs/stat_collection_job.rb +++ b/app/jobs/stat_collection_job.rb @@ -7,8 +7,8 @@ class StatCollectionJob < PeriodicJob STAT_NAME = 'stat_collection_job' - def perform(site_id:, once: true) - @site = Site.find site_id + def perform(site:, once: true) + @site = site beginning = beginning_of_interval stat = site.stats.create! name: STAT_NAME @@ -22,7 +22,7 @@ class StatCollectionJob < PeriodicJob rollup.average(:seconds) end - dimensions = { site_id: site_id } + dimensions = { site_id: site.id } reduce_rollup(name: 'builds', operation: :sum, dimensions: dimensions) reduce_rollup(name: 'space_used', operation: :average, dimensions: dimensions) diff --git a/app/jobs/uri_collection_job.rb b/app/jobs/uri_collection_job.rb index 4cbbf593..92d788bc 100644 --- a/app/jobs/uri_collection_job.rb +++ b/app/jobs/uri_collection_job.rb @@ -16,8 +16,8 @@ class UriCollectionJob < PeriodicJob IMAGES = %w[.png .jpg .jpeg .gif .webp .jfif].freeze STAT_NAME = 'uri_collection_job' - def perform(site_id:, once: true) - @site = Site.find site_id + def perform(site:, once: true) + @site = site # Obtener el principio del intervalo anterior beginning_of_interval diff --git a/app/lib/jekyll/readers/data_reader_decorator.rb b/app/lib/jekyll/readers/data_reader_decorator.rb index 9fed7ac7..2a2a8fc2 100644 --- a/app/lib/jekyll/readers/data_reader_decorator.rb +++ b/app/lib/jekyll/readers/data_reader_decorator.rb @@ -14,6 +14,8 @@ module Jekyll extend ActiveSupport::Concern included do + DATA_EXTENSIONS = %w[.yaml .yml .json .csv .tsv].freeze + def read_data_to(dir, data) return unless File.directory?(dir) && !@entry_filter.symlink?(dir) @@ -24,7 +26,7 @@ module Jekyll if File.directory?(path) read_data_to(path, data[sanitize_filename(entry)] = {}) - else + elsif DATA_EXTENSIONS.include?(File.extname(entry)) key = sanitize_filename(File.basename(entry, ".*")) data[key] = read_data_file(path) end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 8369550d..1f1d453e 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -10,7 +10,7 @@ class ApplicationMailer < ActionMailer::Base private def site - @site ||= Site.find @params[:site_id] + @site ||= @params[:site] end def inline_logo! diff --git a/app/mailers/deploy_mailer.rb b/app/mailers/deploy_mailer.rb index 37748b42..abf6932c 100644 --- a/app/mailers/deploy_mailer.rb +++ b/app/mailers/deploy_mailer.rb @@ -13,8 +13,7 @@ class DeployMailer < ApplicationMailer # rubocop:disable Metrics/AbcSize def deployed(deploys = {}) - usuarie = Usuarie.find(params[:usuarie]) - site = usuarie.sites.find(params[:site]) + usuarie = params[:usuarie] hostname = site.hostname deploys ||= {} diff --git a/app/mailers/invitadx_mailer.rb b/app/mailers/invitadx_mailer.rb deleted file mode 100644 index cfb80a55..00000000 --- a/app/mailers/invitadx_mailer.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -class InvitadxMailer < ApplicationMailer - def confirmation_required - @invitadx = params[:invitadx] - @site = params[:site] - mail from: "#{@site.config.dig('title')} <#{ENV.fetch('DEFAULT_FROM', 'sutty@kefir.red')}>", to: @invitadx.email, subject: t('.subject') - end -end diff --git a/app/models/activity_pub.rb b/app/models/activity_pub.rb index cd893406..7f8155cd 100644 --- a/app/models/activity_pub.rb +++ b/app/models/activity_pub.rb @@ -64,7 +64,7 @@ class ActivityPub < ApplicationRecord # 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| + links = object['url'].map.with_index do |link, _i| case link when Hash then link else { 'href' => link.to_s } @@ -93,7 +93,8 @@ class ActivityPub < ApplicationRecord # 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 }) + ExceptionNotifier.notify_exception(e, + data: { site: site.name, activity_pub: id, activity: activities.first.uri }) end # Se puede volver a pausa en caso de actualización remota, para diff --git a/app/models/activity_pub/fediblock.rb b/app/models/activity_pub/fediblock.rb index 17897d79..e66e6e60 100644 --- a/app/models/activity_pub/fediblock.rb +++ b/app/models/activity_pub/fediblock.rb @@ -35,9 +35,9 @@ class ActivityPub validates_inclusion_of :format, in: %w[mastodon fediblock none] HOSTNAME_HEADERS = { - 'mastodon' => '#domain', + 'mastodon' => '#domain', 'fediblock' => 'domain' - } + }.freeze def client @client ||= Client.new diff --git a/app/models/activity_pub/object.rb b/app/models/activity_pub/object.rb index d37c9b88..b10b4431 100644 --- a/app/models/activity_pub/object.rb +++ b/app/models/activity_pub/object.rb @@ -39,13 +39,14 @@ class ActivityPub def referenced(site) require 'distributed_press/v1/social/referenced_object' - @referenced ||= DistributedPress::V1::Social::ReferencedObject.new(object: content, dereferencer: site.social_inbox.dereferencer) + @referenced ||= DistributedPress::V1::Social::ReferencedObject.new(object: content, + dereferencer: site.social_inbox.dereferencer) end private def uri_is_content_id? - return if self.uri == content['id'] + return if uri == content['id'] errors.add(:activity_pub_objects, 'El ID del objeto no coincide con su URI') end diff --git a/app/models/activity_pub/remote_flag.rb b/app/models/activity_pub/remote_flag.rb index c3cc0fb0..d6348650 100644 --- a/app/models/activity_pub/remote_flag.rb +++ b/app/models/activity_pub/remote_flag.rb @@ -40,7 +40,8 @@ class ActivityPub def content { '@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), + 'id' => Rails.application.routes.url_helpers.v1_activity_pub_remote_flag_url(self, + host: site.social_inbox_hostname), 'type' => 'Flag', 'actor' => main_site.social_inbox.actor_id, 'content' => message.to_s, @@ -53,7 +54,7 @@ class ActivityPub # # @return [Site] def main_site - @main_site ||= Site.find(ENV.fetch('PANEL_ACTOR_SITE_ID') { 1 }) + @main_site ||= Site.find(ENV.fetch('PANEL_ACTOR_SITE_ID', 1)) end end end diff --git a/app/models/application_record.rb b/app/models/application_record.rb index 71fbba5b..6662ddeb 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -2,4 +2,28 @@ class ApplicationRecord < ActiveRecord::Base self.abstract_class = true + + # Obtener una lista filtrada de atributos al momento de serializar + # + # @return [String] + def to_yaml(options = {}) + pruned_attributes.to_yaml(options) + end + + # Devuelve todos los atributos menos los filtrados + # + # @return [Hash] + def pruned_attributes + self.class.inspection_filter.filter(serializable_hash) + end + + # @param coder [Psych::Coder] + # @return nil + def encode_with(coder) + pruned_attributes.each_pair do |attr, value| + coder[attr] = value + end + + nil + end end diff --git a/app/models/concerns/tienda.rb b/app/models/concerns/tienda.rb index 86174c9a..a3e6007a 100644 --- a/app/models/concerns/tienda.rb +++ b/app/models/concerns/tienda.rb @@ -17,7 +17,7 @@ module Tienda return t if new_record? - t.blank? ? 'https://' + name + '.' + ENV.fetch('TIENDA', 'tienda.sutty.nl') : t + t.blank? ? "https://#{name}.#{ENV.fetch('TIENDA', 'tienda.sutty.nl')}" : t end end end diff --git a/app/models/fediblock_state.rb b/app/models/fediblock_state.rb index 82912f76..02dee2d8 100644 --- a/app/models/fediblock_state.rb +++ b/app/models/fediblock_state.rb @@ -45,7 +45,8 @@ class FediblockState < ApplicationRecord private def block_instances! - ActivityPub::InstanceModerationJob.perform_later(site: site, hostnames: fediblock.hostnames, perform_remotely: false) + ActivityPub::InstanceModerationJob.perform_later(site: site, hostnames: fediblock.hostnames, + perform_remotely: false) end # Pausar todas las moderaciones de las instancias que no estén diff --git a/app/models/instance_moderation.rb b/app/models/instance_moderation.rb index 5a1a5ed6..c1192615 100644 --- a/app/models/instance_moderation.rb +++ b/app/models/instance_moderation.rb @@ -16,7 +16,9 @@ class InstanceModeration < ApplicationRecord state :blocked error_on_all_events do |e| - ExceptionNotifier.notify_exception(e, data: { site: site.name, instance: instance.hostname, instance_moderation: id }) + ExceptionNotifier.notify_exception(e, + data: { site: site.name, instance: instance.hostname, + instance_moderation: id }) end after_all_events do diff --git a/app/models/log_entry.rb b/app/models/log_entry.rb index 9685e0d0..7525177a 100644 --- a/app/models/log_entry.rb +++ b/app/models/log_entry.rb @@ -11,7 +11,7 @@ class LogEntry < ApplicationRecord def resend return if sent - ContactJob.perform_later site_id, params[:form], params + ContactJob.perform_later site, params[:form], params end def params diff --git a/app/models/metadata_template.rb b/app/models/metadata_template.rb index 48a5c586..a95f7e12 100644 --- a/app/models/metadata_template.rb +++ b/app/models/metadata_template.rb @@ -142,7 +142,7 @@ MetadataTemplate = Struct.new(:site, :document, :name, :label, :type, # En caso de que algún campo necesite realizar acciones antes de ser # guardado def save - if !changed? + unless changed? self[:value] = document_value if private? return true diff --git a/app/models/site.rb b/app/models/site.rb index e5205b10..fdb87659 100644 --- a/app/models/site.rb +++ b/app/models/site.rb @@ -11,8 +11,11 @@ class Site < ApplicationRecord include Site::BuildStats include Site::LayoutOrdering include Site::SocialDistributedPress + include Site::DefaultOptions include Tienda + self.filter_attributes += [/_key/, /_ciphertext\z/] + # Cifrar la llave privada que cifra y decifra campos ocultos. Sutty # tiene acceso pero los datos se guardan cifrados en el sitio. Esto # protege información privada en repositorios públicos, pero no la @@ -235,11 +238,11 @@ class Site < ApplicationRecord # layouts. Si pasamos un layout que no existe, obtenemos un # NoMethodError @layouts_struct ||= Struct.new(*layout_keys, keyword_init: true) - @layouts ||= @layouts_struct.new(**data['layouts'].map do |name, metadata| + @layouts ||= @layouts_struct.new(**data['layouts'].to_h do |name, metadata| [name.to_sym, Layout.new(site: self, name: name.to_sym, meta: metadata.delete('meta')&.with_indifferent_access, metadata: metadata.with_indifferent_access)] - end.to_h) + end) end # TODO: Si la estructura de datos no existe, vamos a producir una @@ -314,7 +317,7 @@ class Site < ApplicationRecord end def reload - super.tap do |s| + super.tap do |_s| reload_jekyll! end self @@ -403,7 +406,7 @@ class Site < ApplicationRecord def clone_skel! return if jekyll? - Rugged::Repository.clone_at(ENV['SKEL_SUTTY'], path, checkout_branch: design.gem) + Rugged::Repository.clone_at(ENV.fetch('SKEL_SUTTY', nil), path, checkout_branch: design.gem) # Necesita un bloque repository.rugged.remotes.rename('origin', 'upstream') {} @@ -497,11 +500,11 @@ class Site < ApplicationRecord deploy_local = deploys.find_by_type('DeployLocal') deploy_local.git_lfs - if !gems_installed? || gemfile_updated? || gemfile_lock_updated? - deploy_local.bundle - touch - FileUtils.touch(gemfile_path) - end + return unless !gems_installed? || gemfile_updated? || gemfile_lock_updated? + + deploy_local.bundle + touch + FileUtils.touch(gemfile_path) end def gem_path diff --git a/app/models/site/default_options.rb b/app/models/site/default_options.rb new file mode 100644 index 00000000..3e392782 --- /dev/null +++ b/app/models/site/default_options.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require 'dry-schema' + +class Site + # Las opciones por defecto se aplican durante la creación del sitio y + # luego se permite a les usuaries modificarlas según quieran. Por el + # momento las opciones nuevas que aparezcan no modifican un sitio que + # ya existe. + module DefaultOptions + extend ActiveSupport::Concern + + Schema = Dry::Schema.Params do + optional(:colaboracion_anonima).value(:bool) + optional(:contact).value(:bool) + optional(:acepta_invitades).value(:bool) + optional(:slugify_mode).value(included_in?: Jekyll::Utils::SLUGIFY_MODES) + optional(:pagination).value(:bool) + end + + included do + validate :validate_options_from_theme!, if: :persisted? + + # @return [Dry::Schema::Result] + def options_from_theme + @options_from_theme ||= Schema.call(data['sutty']) + end + + def update_options_from_theme + return true if options_from_theme.to_h.blank? + + update(**options_from_theme.to_h) + end + + private + + def validate_options_from_theme! + options_from_theme.errors.each do |error| + errors.add(:default_options, "#{error.path.map(&:to_s).join('/')} #{error} (#{error.input})") + end + end + end + end +end diff --git a/app/models/social_inbox.rb b/app/models/social_inbox.rb index 183ebfb0..adeedffc 100644 --- a/app/models/social_inbox.rb +++ b/app/models/social_inbox.rb @@ -45,7 +45,7 @@ class SocialInbox # @param url [String] # @return [DistributedPress::V1::Social::Client] def client_for(url) - raise "Falló generar un cliente" if url.blank? + raise 'Falló generar un cliente' if url.blank? @client_for ||= {} @client_for[url] ||= @@ -54,7 +54,7 @@ class SocialInbox public_key_url: public_key_url, private_key_pem: site.private_key_pem, logger: Rails.logger, - cache_store: HTTParty::Cache::Store::Redis.new(redis_url: ENV['REDIS_SERVER']) + cache_store: HTTParty::Cache::Store::Redis.new(redis_url: ENV.fetch('REDIS_SERVER', nil)) ) end diff --git a/app/models/usuarie.rb b/app/models/usuarie.rb index 42f20c0b..4856f17f 100644 --- a/app/models/usuarie.rb +++ b/app/models/usuarie.rb @@ -21,6 +21,8 @@ class Usuarie < ApplicationRecord has_many :blazer_audits, foreign_key: 'user_id', class_name: 'Blazer::Audit' has_many :blazer_queries, foreign_key: 'creator_id', class_name: 'Blazer::Query' + self.filter_attributes += [/\Aemail\z/, /\Aencrypted_password\z/] + def name email.split('@', 2).first end @@ -74,10 +76,10 @@ class Usuarie < ApplicationRecord # Si le usuarie (re)confirma su cuenta con una invitación pendiente, # considerarla aceptada también. def accept_invitation_after_confirmation! - if confirmed? - self.invitation_token = nil - self.invitation_accepted_at ||= Time.now.utc - end + return unless confirmed? + + self.invitation_token = nil + self.invitation_accepted_at ||= Time.now.utc end # Muestra un error si el idioma no está disponible al cambiar el @@ -85,7 +87,7 @@ class Usuarie < ApplicationRecord # # @return [nil] def locale_available! - return if I18n.locale_available? self.lang + return if I18n.locale_available? lang errors.add(:lang, I18n.t('activerecord.errors.models.usuarie.attributes.lang.not_available')) nil diff --git a/app/services/site_service.rb b/app/services/site_service.rb index 0e9b7f62..a1007e04 100644 --- a/app/services/site_service.rb +++ b/app/services/site_service.rb @@ -5,7 +5,7 @@ SiteService = Struct.new(:site, :usuarie, :params, keyword_init: true) do def deploy site.enqueue! - DeployJob.perform_later site.id + DeployJob.perform_later site end # Crea un sitio, agrega un rol nuevo y guarda los cambios a la @@ -31,6 +31,7 @@ SiteService = Struct.new(:site, :usuarie, :params, keyword_init: true) do add_role_to_deploys! role site.save && + site.update_options_from_theme && site.config.write && commit_config(action: :create) && site.reset.nil? && @@ -236,8 +237,6 @@ SiteService = Struct.new(:site, :usuarie, :params, keyword_init: true) do end end - private - # Asignar un rol a cada deploy si no lo tenía ya def add_role_to_deploys!(role = current_role) site.deploys.each do |deploy| @@ -249,7 +248,7 @@ SiteService = Struct.new(:site, :usuarie, :params, keyword_init: true) do @current_role ||= usuarie.rol_for_site(site) end - def with_all_locales(&block) + def with_all_locales site.locales.map do |locale| next unless I18n.available_locales.include? locale diff --git a/app/views/components/_profiles_btn_box.haml b/app/views/components/_profiles_btn_box.haml index 8fc8dd39..2023de96 100644 --- a/app/views/components/_profiles_btn_box.haml +++ b/app/views/components/_profiles_btn_box.haml @@ -2,7 +2,7 @@ .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}?") + - possible = actor_moderation.public_send(:"may_#{actor_event}?") %div{ class: local.dig(actor_event, :class) } = render 'components/btn_base', text: t(".text_#{actor_event}"), diff --git a/app/views/env/index.js.haml b/app/views/env/index.js.haml index 597ba53f..8627fa4f 100644 --- a/app/views/env/index.js.haml +++ b/app/views/env/index.js.haml @@ -2,7 +2,7 @@ = cache @site do :plain window.env = { - AIRBRAKE_SITE_ID: #{@site.id}, - AIRBRAKE_API_KEY: "#{@site.airbrake_api_key}", + AIRBRAKE_PROJECT_ID: #{@site.id}, + AIRBRAKE_PROJECT_KEY: "#{@site.airbrake_api_key}", PANEL_URL: "#{ENV['PANEL_URL']}" } diff --git a/app/views/layouts/_breadcrumb.haml b/app/views/layouts/_breadcrumb.haml index 7fc49888..2f966b26 100644 --- a/app/views/layouts/_breadcrumb.haml +++ b/app/views/layouts/_breadcrumb.haml @@ -13,8 +13,8 @@ - else = link_to crumb.name, crumb.url, class: 'line-clamp-1' - %ul.navbar-nav.order-1.order-md-2 - - if @current_usuarie || current_usuarie + - if @current_usuarie || current_usuarie + %ul.navbar-nav.order-1.order-md-2 - if @site&.tienda? %li.nav-item = link_to t('.tienda'), @site.tienda_url, diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 16765965..eaa15eb4 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -15,7 +15,7 @@ = csrf_meta_tags = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' = stylesheet_link_tag 'dark', rel: 'alternate stylesheet', media: 'all', 'data-turbolinks-track': 'reload', title: t('dark') - = javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' + = javascript_pack_tag 'application', 'data-turbolinks-track': 'reload', defer: true = stylesheet_pack_tag 'application', 'data-turbolinks-track': 'reload' = favicon_link_tag 'sutty_cuadrada.png', rel: 'apple-touch-icon', type: 'image/png' = render 'layouts/link_rel_alternate' diff --git a/app/views/posts/index.haml b/app/views/posts/index.haml index eac5c513..0a868890 100644 --- a/app/views/posts/index.haml +++ b/app/views/posts/index.haml @@ -43,7 +43,6 @@ = render 'schemas/row', site: @site, schema: schema, filter: @filter_params - - if policy(@site_stat).index? = link_to t('stats.index.title'), site_stats_path(@site), class: 'btn btn-secondary' @@ -136,15 +135,16 @@ %span{ lang: post.locale, dir: dir }= category = '/' unless post.front_matter['categories'].last == category - %td.text-nowrap - = post.created_at.strftime('%F') - %br/ - = post.order - %td.text-nowrap - - if @usuarie || policy(post).edit? - = link_to t('posts.edit'), edit_site_post_path(@site, post.path), class: 'btn btn-secondary btn-block' - - if @usuarie || policy(post).destroy? - = link_to t('posts.destroy'), site_post_path(@site, post.path), class: 'btn btn-secondary btn-block', method: :delete, data: { confirm: t('posts.confirm_destroy') } + %td.text-nowrap + = post.created_at.strftime('%F') + %br/ + = post.order + %td.text-nowrap + .d-flex.flex-row.align-items-start + - if @usuarie || policy(post).edit? + = link_to t('posts.edit_post'), edit_site_post_path(@site, post.path), class: 'btn btn-secondary' + - if @usuarie || policy(post).destroy? + = link_to t('posts.destroy'), site_post_path(@site, post.path), class: 'btn btn-secondary', method: :delete, data: { confirm: t('posts.confirm_destroy') } -# Rescatar cualquier error en un post, notificarlo e ignorar su renderización. diff --git a/config/environments/production.rb b/config/environments/production.rb index bc7cecd7..5b0667a5 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -62,7 +62,7 @@ Rails.application.configure do config.log_tags = %i[request_id] # Use a different cache store in production. - config.cache_store = :redis_cache_store, { url: ENV['REDIS_SERVER'] } + config.cache_store = :redis_cache_store, { url: ENV.fetch('REDIS_SERVER', nil) } config.action_mailer.perform_caching = false @@ -87,7 +87,7 @@ Rails.application.configure do config.lograge.enabled = true # Use default logging formatter so that PID and timestamp are not # suppressed. - config.log_formatter = ::Logger::Formatter.new + config.log_formatter = Logger::Formatter.new # Use a different logger for distributed setups. require 'syslog/logger' @@ -140,9 +140,10 @@ Rails.application.configure do domain: ENV.fetch('SUTTY', 'sutty.nl'), enable_starttls_auto: false } - config.action_mailer.default_options = { from: ENV.fetch('DEFAULT_FROM', "noreply@sutty.nl") } + config.action_mailer.default_options = { from: ENV.fetch('DEFAULT_FROM', 'noreply@sutty.nl') } - config.middleware.use ExceptionNotification::Rack, gitlab: {}, error_grouping: true, 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' diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb index 2fc446ff..b1d9f2b0 100644 --- a/config/initializers/filter_parameter_logging.rb +++ b/config/initializers/filter_parameter_logging.rb @@ -5,4 +5,5 @@ # Configure sensitive parameters which will be filtered from the log file. Rails.application.config.filter_parameters += %i[ password passw secret token _key crypt salt certificate otp ssn key + _pem _ciphertext email ] diff --git a/config/initializers/que_web.rb b/config/initializers/que_web.rb index 192256db..a6b87cf8 100644 --- a/config/initializers/que_web.rb +++ b/config/initializers/que_web.rb @@ -1,5 +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']] + [user, password] == [ENV.fetch('HTTP_BASIC_USER', nil), ENV.fetch('HTTP_BASIC_PASSWORD', nil)] end diff --git a/db/migrate/20231101200026_add_default_to_distributed_press_publisher.rb b/db/migrate/20231101200026_add_default_to_distributed_press_publisher.rb index ada9392f..fd833acb 100644 --- a/db/migrate/20231101200026_add_default_to_distributed_press_publisher.rb +++ b/db/migrate/20231101200026_add_default_to_distributed_press_publisher.rb @@ -5,7 +5,7 @@ class AddDefaultToDistributedPressPublisher < ActiveRecord::Migration[6.1] def up add_column :distributed_press_publishers, :default, :boolean, default: false - DistributedPressPublisher.last.update(default: true) + DistributedPressPublisher.last&.update(default: true) end def down diff --git a/db/migrate/20240227142019_create_fediblock_states.rb b/db/migrate/20240227142019_create_fediblock_states.rb index c99cf63d..1e718343 100644 --- a/db/migrate/20240227142019_create_fediblock_states.rb +++ b/db/migrate/20240227142019_create_fediblock_states.rb @@ -16,9 +16,7 @@ class CreateFediblockStates < ActiveRecord::Migration[6.1] # Todas las listas están activas por defecto DeploySocialDistributedPress.find_each do |deploy| ActivityPub::Fediblock.find_each do |fediblock| - FediblockState.create(site: deploy.site, fediblock: fediblock, aasm_state: 'disabled').tap do |f| - f.enable! - end + FediblockState.create(site: deploy.site, fediblock: fediblock, aasm_state: 'disabled').tap(&:enable!) end end end diff --git a/db/migrate/20240313204105_brs_decompressor_corrupted_source_error.rb b/db/migrate/20240313204105_brs_decompressor_corrupted_source_error.rb index a0c29311..e22d759b 100644 --- a/db/migrate/20240313204105_brs_decompressor_corrupted_source_error.rb +++ b/db/migrate/20240313204105_brs_decompressor_corrupted_source_error.rb @@ -4,9 +4,11 @@ # 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? + 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) + QueJob.where("last_error_message like '%BRS::DecompressorCorruptedSourceError%'").update_all(error_count: 0, + run_at: Time.now) end def down; end diff --git a/db/migrate/20240314205923_fix_activity_type.rb b/db/migrate/20240314205923_fix_activity_type.rb index 042de8eb..e6640ff8 100644 --- a/db/migrate/20240314205923_fix_activity_type.rb +++ b/db/migrate/20240314205923_fix_activity_type.rb @@ -4,7 +4,9 @@ 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) + ActivityPub::Activity.where(Arel.sql("content->>'type' = '#{type}'")).update_all( + type: "ActivityPub::Activity::#{type}", updated_at: Time.now + ) end end diff --git a/db/migrate/20240318183846_fix_duplicate_objects.rb b/db/migrate/20240318183846_fix_duplicate_objects.rb index 88d23c6f..9f02c3db 100644 --- a/db/migrate/20240318183846_fix_duplicate_objects.rb +++ b/db/migrate/20240318183846_fix_duplicate_objects.rb @@ -3,7 +3,7 @@ # 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| + ActivityPub::Object.group(:uri).count.select { |_, v| v > 1 }.each_key do |uri| objects = ActivityPub::Object.where(uri: uri) deleted_ids = objects[1..].map(&:delete).map(&:id) diff --git a/db/migrate/20240319124212_add_fedipact_to_fediblocks.rb b/db/migrate/20240319124212_add_fedipact_to_fediblocks.rb index d78439b2..f751123a 100644 --- a/db/migrate/20240319124212_add_fedipact_to_fediblocks.rb +++ b/db/migrate/20240319124212_add_fedipact_to_fediblocks.rb @@ -14,9 +14,7 @@ class AddFedipactToFediblocks < ActiveRecord::Migration[6.1] ) DeploySocialDistributedPress.find_each do |deploy| - FediblockState.create(site: deploy.site, fediblock: fedipact, aasm_state: 'disabled').tap do |f| - f.enable! - end + FediblockState.create(site: deploy.site, fediblock: fedipact, aasm_state: 'disabled').tap(&:enable!) end end diff --git a/db/migrate/20240319144735_add_missing_unique_indexes.rb b/db/migrate/20240319144735_add_missing_unique_indexes.rb index 7d18c8e8..2f6ef1aa 100644 --- a/db/migrate/20240319144735_add_missing_unique_indexes.rb +++ b/db/migrate/20240319144735_add_missing_unique_indexes.rb @@ -4,14 +4,14 @@ # 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| + ActivityPub::Object.group(:uri).count.select { |_, v| v > 1 }.each_key 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| + ActivityPub::Actor.group(:uri).count.select { |_, v| v > 1 }.each_key do |uri| objects = ActivityPub::Actor.where(uri: uri) deleted_ids = objects[1..].map(&:delete).map(&:id) @@ -21,7 +21,7 @@ class AddMissingUniqueIndexes < ActiveRecord::Migration[6.1] 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| + ActivityPub::Instance.group(:hostname).count.select { |_, v| v > 1 }.each_key do |hostname| objects = ActivityPub::Instance.where(hostname: hostname) deleted_ids = objects[1..].map(&:delete).map(&:id) diff --git a/db/seeds.rb b/db/seeds.rb index 8e8c291f..41474883 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -20,13 +20,13 @@ if CodeOfConduct.count.zero? YAML.safe_load(File.read('db/seeds/codes_of_conduct.yml')).each do |coc| CodeOfConduct.new(**coc).save! end -end +end if PrivacyPolicy.count.zero? YAML.safe_load(File.read('db/seeds/privacy_policies.yml')).each do |pp| PrivacyPolicy.new(**pp).save! end -end +end YAML.safe_load(File.read('db/seeds/activity_pub/fediblocks.yml')).each do |fediblock| ActivityPub::Fediblock.find_or_create_by(id: fediblock['id']).tap do |f| diff --git a/db/seeds/designs.yml b/db/seeds/designs.yml index d1ae458e..2b7b6492 100644 --- a/db/seeds/designs.yml +++ b/db/seeds/designs.yml @@ -91,6 +91,14 @@ description_en: "We're working towards adding more themes for you to use. [Contact us!](https://sutty.nl/en/#contact)" description_es: "Estamos trabajando para que puedas tener más diseños. [¡Escribinos!](https://sutty.nl/#contacto)" priority: '3' +- name_en: 'Worker-recovered factory' + name_es: 'Empresa recuperada' + gem: 'empresa-recuperada-jekyll-theme' + url: 'https://empresa-recuperada.sutty.nl/' + disabled: true + description_en: "A template for [empresas recuperadas](https://en.wikipedia.org/wiki/Workers%27_self-management#Empresas_recuperadas_movement). We're working towards adding more themes for you to use. [Contact us!](https://sutty.nl/en/#contact)" + description_es: "Estamos trabajando para que puedas tener más diseños. [¡Escribinos!](https://sutty.nl/#contacto)" + priority: '3' - name_en: 'More themes' name_es: 'Más plantillas' gem: 'sutty-theme-own' diff --git a/lib/tasks/stats.rake b/lib/tasks/stats.rake index 9461782a..fbcb5fa4 100644 --- a/lib/tasks/stats.rake +++ b/lib/tasks/stats.rake @@ -3,9 +3,9 @@ namespace :stats do desc 'Process stats' task process_all: :environment do - Site.all.pluck(:id).each do |site_id| - UriCollectionJob.perform_now site_id: site_id, once: true - StatCollectionJob.perform_now site_id: site_id, once: true + Site.all.find_each do |site| + UriCollectionJob.perform_now site: site, once: true + StatCollectionJob.perform_now site: site, once: true end end end diff --git a/package.json b/package.json index eea0473f..088316bc 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "@rails/activestorage": "^6.1.3-1", "@rails/ujs": "^6.1.3-1", "@rails/webpacker": "5.4.4", - "@suttyweb/editor": "^0.1.27", + "@suttyweb/editor": "^0.1.29", "babel-loader": "^8.2.2", "bs-custom-file-input": "^1.3.4", "chart.js": "^3.5.1", diff --git a/yarn.lock b/yarn.lock index 7a81a221..fc6ae7cb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1975,10 +1975,10 @@ resolved "https://registry.npmjs.org/@stimulus/webpack-helpers/-/webpack-helpers-1.1.1.tgz" integrity sha512-XOkqSw53N9072FLHvpLM25PIwy+ndkSSbnTtjKuyzsv8K5yfkFB2rv68jU1pzqYa9FZLcvZWP4yazC0V38dx9A== -"@suttyweb/editor@^0.1.27": - version "0.1.27" - resolved "https://registry.yarnpkg.com/@suttyweb/editor/-/editor-0.1.27.tgz#9415a0b767e72dbe4fbf42ce87e62fb8f5125c31" - integrity sha512-Ts9TZtGiRIaHm+ffVBRl+/nuVcANWZNtFsrGacoajgEsagaIyA1cq8qjiNpPoM5ne9vTba3cAaLP04V/uEIhBw== +"@suttyweb/editor@^0.1.29": + version "0.1.29" + resolved "https://registry.yarnpkg.com/@suttyweb/editor/-/editor-0.1.29.tgz#8b5c6ae4e4d546002a96ecd65765d77d2a88d415" + integrity sha512-GshI8wE5UqXge2RhwAUxUXTRLPoOX7US9xVu1aLqT/deT/hDyN9S3PxVn9cJBf7uPHEqBzYXGDKWWF79PLqGHw== dependencies: "@floating-ui/dom" "^1.5.1" linkifyjs "^4.1.1"