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/jobs/application_job.rb b/app/jobs/application_job.rb index 06690c53..d527c9b9 100644 --- a/app/jobs/application_job.rb +++ b/app/jobs/application_job.rb @@ -4,9 +4,10 @@ class ApplicationJob < ActiveJob::Base include Que::ActiveJob::JobExtensions - 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 3044b59f..66cccd1b 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| @@ -88,12 +78,12 @@ class DeployJob < ApplicationJob end end) 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 @@ -123,7 +113,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) @@ -133,8 +123,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 58a4e6b1..72e20be0 100644 --- a/app/jobs/git_pull_job.rb +++ b/app/jobs/git_pull_job.rb @@ -7,6 +7,8 @@ class GitPullJob < ApplicationJob # @param :usuarie [Usuarie] # @return [nil] def perform(site, usuarie) + @site = site + return unless site.repository.origin return unless site.repository.fetch.positive? 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/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/application_record.rb b/app/models/application_record.rb index 71fbba5b..f09c4dd4 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -2,4 +2,11 @@ class ApplicationRecord < ActiveRecord::Base self.abstract_class = true + + # Obtener una lista filtrada de atributos al momento de serializar + # + # @return [String] + def to_yaml(options = {}) + self.class.inspection_filter.filter(serializable_hash).to_yaml(options) + end end 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/site.rb b/app/models/site.rb index 2ee7a8ef..f51d2c6f 100644 --- a/app/models/site.rb +++ b/app/models/site.rb @@ -13,6 +13,8 @@ class Site < ApplicationRecord include Site::SocialDistributedPress 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 @@ -290,11 +292,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 @@ -387,7 +389,7 @@ class Site < ApplicationRecord end def reload - super.tap do |s| + super.tap do |_s| reload_jekyll! end self @@ -477,7 +479,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') {} @@ -577,11 +579,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/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 5c37cfe3..a28e5ee8 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 @@ -222,9 +222,7 @@ SiteService = Struct.new(:site, :usuarie, :params, keyword_init: true) do end end - private - - def with_all_locales(&block) + def with_all_locales site.locales.map do |locale| next unless I18n.available_locales.include? locale 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/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