5
0
Fork 0
mirror of https://0xacab.org/sutty/sutty synced 2024-11-22 19:26:21 +00:00

Merge branch 'issue-14339' into 'rails'

fix: reportar errores de ssh una sola vez

Closes #16059, #16058, #16052, #15871, #15870, #15869, #15868, #15867, #15866, #15865, #15864, #15863, #15862, #15861, #15860, #15859, #15858, #15857, #15856, #15855, #15849, #15848, #15847, #15846, #15845, #14662, #14561, and #14339

See merge request sutty/sutty!255
This commit is contained in:
fauno 2024-05-02 18:59:12 +00:00
commit 08db600726
22 changed files with 82 additions and 97 deletions

View file

@ -18,7 +18,7 @@ module Api
# Si todo salió bien, enviar los correos y redirigir al sitio. # Si todo salió bien, enviar los correos y redirigir al sitio.
# El sitio nos dice a dónde tenemos que ir. # El sitio nos dice a dónde tenemos que ir.
ContactJob.perform_later site.id, ContactJob.perform_later site,
params[:form], params[:form],
contact_params.to_h.symbolize_keys, contact_params.to_h.symbolize_keys,
params[:redirect] params[:redirect]

View file

@ -11,7 +11,7 @@ module Api
# respondemos con lo mismo. # respondemos con lo mismo.
def create def create
if (site&.airbrake_valid? airbrake_token) && !detected_device.bot? 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 params: airbrake_params.to_h
end end

View file

@ -4,9 +4,10 @@
class ApplicationJob < ActiveJob::Base class ApplicationJob < ActiveJob::Base
include Que::ActiveJob::JobExtensions include Que::ActiveJob::JobExtensions
private attr_reader :site
def site # Si falla por cualquier cosa informar y descartar
@site ||= Site.find @params[:site_id] discard_on(Exception) do |job, error|
ExceptionNotifier.notify_exception(error, data: { job: job })
end end
end end

View file

@ -6,10 +6,10 @@ class BacktraceJob < ApplicationJob
EMPTY_SOURCEMAP = { 'mappings' => '' }.freeze EMPTY_SOURCEMAP = { 'mappings' => '' }.freeze
attr_reader :params, :site_id attr_reader :params
def perform(site_id:, params:) def perform(site:, params:)
@site_id = site_id @site = site
@params = params @params = params
unless sources.empty? unless sources.empty?
@ -44,10 +44,6 @@ class BacktraceJob < ApplicationJob
private private
def site
@site ||= Site.find_by_id(site_id)
end
# Obtiene todos los archivos del backtrace solo si los puede descargar # Obtiene todos los archivos del backtrace solo si los puede descargar
# desde fuentes seguras. # desde fuentes seguras.
# #
@ -59,9 +55,7 @@ class BacktraceJob < ApplicationJob
x['backtrace'] x['backtrace']
end.flatten.map do |x| end.flatten.map do |x|
x['file'].split('@').last x['file'].split('@').last
end.uniq.select do |x| end.uniq.grep(%r{\Ahttps://})
%r{\Ahttps://} =~ x
end
end end
# Descarga y devuelve los datos de un archivo # Descarga y devuelve los datos de un archivo

View file

@ -5,10 +5,8 @@ class ContactJob < ApplicationJob
# @param [Integer] # @param [Integer]
# @param [String] # @param [String]
# @param [Hash] # @param [Hash]
def perform(site_id, form_name, form, origin = nil) def perform(site, form_name, form, origin = nil)
# Retrocompabilidad al actualizar a 2.7.1 @site = site
# @see ApplicationJob#site
@params = { site_id: site_id }
# Sanitizar los valores # Sanitizar los valores
form.each_key do |key| form.each_key do |key|
@ -23,7 +21,7 @@ class ContactJob < ApplicationJob
usuaries.each_slice(10) do |u| usuaries.each_slice(10) do |u|
ContactMailer.with(form_name: form_name, ContactMailer.with(form_name: form_name,
form: form, form: form,
site_id: site_id, site: site,
usuaries_emails: u, usuaries_emails: u,
origin: origin) origin: origin)
.notify_usuaries.deliver_now .notify_usuaries.deliver_now

View file

@ -11,44 +11,36 @@ class DeployJob < ApplicationJob
# Lanzar lo antes posible # Lanzar lo antes posible
self.priority = 10 self.priority = 10
def handle_error(error) retry_on DeployAlreadyRunningException, wait: 1.minute
case error discard_on DeployTimedOutException
when DeployAlreadyRunningException then retry_in 1.minute
when DeployTimedOutException then expire
else super
end
end
# rubocop:disable Metrics/MethodLength # rubocop:disable Metrics/MethodLength
def perform(site, notify: true, time: Time.now, output: false) def perform(site, notify: true, time: Time.now, output: false)
@output = output @site = site
ActiveRecord::Base.connection_pool.with_connection do ActiveRecord::Base.connection_pool.with_connection do
@site = Site.find(site)
# Si ya hay una tarea corriendo, aplazar esta. Si estuvo # Si ya hay una tarea corriendo, aplazar esta. Si estuvo
# esperando más de 10 minutos, recuperar el estado anterior. # esperando más de 10 minutos, recuperar el estado anterior.
# #
# Como el trabajo actual se aplaza al siguiente, arrastrar la # Como el trabajo actual se aplaza al siguiente, arrastrar la
# hora original para poder ir haciendo timeouts. # hora original para poder ir haciendo timeouts.
if @site.building? if site.building?
notify = false notify = false
if 10.minutes.ago >= time raise DeployAlreadyRunningException unless 10.minutes.ago >= time
raise DeployTimedOutException,
"#{@site.name} la tarea estuvo más de 10 minutos esperando, volviendo al estado original" raise DeployTimedOutException,
else "#{site.name} la tarea estuvo más de 10 minutos esperando, volviendo al estado original"
raise DeployAlreadyRunningException
end
end end
@deployed = {} @deployed = {}
@site.update status: 'building' site.update status: 'building'
@site.deployment_list.each do |d| site.deployment_list.each do |d|
begin begin
raise DeployException, 'Una dependencia falló' if failed_dependencies? d 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 seconds = d.build_stats.last.try(:seconds) || 0
size = d.size size = d.size
urls = d.urls.map do |url| urls = d.urls.map do |url|
@ -57,9 +49,7 @@ class DeployJob < ApplicationJob
nil nil
end.compact end.compact
if d == @site.deployment_list.last && !status raise DeployException, 'Falló la compilación' if d == site.deployment_list.last && !status
raise DeployException, 'Falló la compilación'
end
rescue StandardError => e rescue StandardError => e
status = false status = false
seconds ||= 0 seconds ||= 0
@ -78,9 +68,9 @@ class DeployJob < ApplicationJob
} }
end 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 << (%w[type] + @deployed.values.first.keys)
t.add_separator t.add_separator
@deployed.each do |type, row| @deployed.each do |type, row|
@ -88,12 +78,12 @@ class DeployJob < ApplicationJob
end end
end) end)
ensure ensure
if @site.present? if site.present?
@site.update status: 'waiting' site.update status: 'waiting'
notify_usuaries if notify notify_usuaries if notify
puts "\a" if @output puts "\a" if output
end end
end end
end end
@ -123,7 +113,7 @@ class DeployJob < ApplicationJob
# @param :deploy [Deploy] # @param :deploy [Deploy]
def notify_exception(exception, deploy = nil) def notify_exception(exception, deploy = nil)
data = { data = {
site: @site.id, site: site.name,
deploy: deploy&.type, deploy: deploy&.type,
log: deploy&.build_stats&.last&.log, log: deploy&.build_stats&.last&.log,
failed_dependencies: (failed_dependencies(deploy) if deploy) failed_dependencies: (failed_dependencies(deploy) if deploy)
@ -133,8 +123,10 @@ class DeployJob < ApplicationJob
end end
def notify_usuaries def notify_usuaries
@site.roles.where(rol: 'usuarie', temporal: false).pluck(:usuarie_id).each do |usuarie| usuarie_ids = site.roles.where(rol: 'usuarie', temporal: false).pluck(:usuarie_id)
DeployMailer.with(usuarie: usuarie, site: @site.id)
Usuarie.where(id: usuarie_ids).find_each do |usuarie|
DeployMailer.with(usuarie: usuarie, site: site)
.deployed(@deployed) .deployed(@deployed)
.deliver_now .deliver_now
end end

View file

@ -7,6 +7,8 @@ class GitPullJob < ApplicationJob
# @param :usuarie [Usuarie] # @param :usuarie [Usuarie]
# @return [nil] # @return [nil]
def perform(site, usuarie) def perform(site, usuarie)
@site = site
return unless site.repository.origin return unless site.repository.origin
return unless site.repository.fetch.positive? return unless site.repository.fetch.positive?

View file

@ -6,6 +6,8 @@ class GitPushJob < ApplicationJob
# @param :site [Site] # @param :site [Site]
# @return [nil] # @return [nil]
def perform(site) def perform(site)
site.repository.push if site.repository.origin @site = site
site.repository.push if site.repository.origin
end end
end end

View file

@ -15,8 +15,7 @@
# Lo mismo para salir de mantenimiento, agregando el atributo # Lo mismo para salir de mantenimiento, agregando el atributo
# are_we_back: true al crear el Maintenance. # are_we_back: true al crear el Maintenance.
class MaintenanceJob < ApplicationJob class MaintenanceJob < ApplicationJob
def perform(maintenance_id:) def perform(maintenance:)
maintenance = Maintenance.find(maintenance_id)
# Decidir cuál vamos a enviar según el estado de Maintenance # Decidir cuál vamos a enviar según el estado de Maintenance
mailer = maintenance.are_we_back ? :were_back : :notice mailer = maintenance.are_we_back ? :were_back : :notice

View file

@ -6,9 +6,6 @@ class PeriodicJob < ApplicationJob
STARTING_INTERVAL = Stat::INTERVALS.first STARTING_INTERVAL = Stat::INTERVALS.first
# Tener el sitio a mano
attr_reader :site
# Descartar y notificar si pasó algo más. # Descartar y notificar si pasó algo más.
# #
# XXX: En realidad deberíamos seguir reintentando? # XXX: En realidad deberíamos seguir reintentando?

View file

@ -7,8 +7,8 @@ class StatCollectionJob < PeriodicJob
STAT_NAME = 'stat_collection_job' STAT_NAME = 'stat_collection_job'
def perform(site_id:, once: true) def perform(site:, once: true)
@site = Site.find site_id @site = site
beginning = beginning_of_interval beginning = beginning_of_interval
stat = site.stats.create! name: STAT_NAME stat = site.stats.create! name: STAT_NAME
@ -22,7 +22,7 @@ class StatCollectionJob < PeriodicJob
rollup.average(:seconds) rollup.average(:seconds)
end end
dimensions = { site_id: site_id } dimensions = { site_id: site.id }
reduce_rollup(name: 'builds', operation: :sum, dimensions: dimensions) reduce_rollup(name: 'builds', operation: :sum, dimensions: dimensions)
reduce_rollup(name: 'space_used', operation: :average, dimensions: dimensions) reduce_rollup(name: 'space_used', operation: :average, dimensions: dimensions)

View file

@ -16,8 +16,8 @@ class UriCollectionJob < PeriodicJob
IMAGES = %w[.png .jpg .jpeg .gif .webp .jfif].freeze IMAGES = %w[.png .jpg .jpeg .gif .webp .jfif].freeze
STAT_NAME = 'uri_collection_job' STAT_NAME = 'uri_collection_job'
def perform(site_id:, once: true) def perform(site:, once: true)
@site = Site.find site_id @site = site
# Obtener el principio del intervalo anterior # Obtener el principio del intervalo anterior
beginning_of_interval beginning_of_interval

View file

@ -10,7 +10,7 @@ class ApplicationMailer < ActionMailer::Base
private private
def site def site
@site ||= Site.find @params[:site_id] @site ||= @params[:site]
end end
def inline_logo! def inline_logo!

View file

@ -13,8 +13,7 @@ class DeployMailer < ApplicationMailer
# rubocop:disable Metrics/AbcSize # rubocop:disable Metrics/AbcSize
def deployed(deploys = {}) def deployed(deploys = {})
usuarie = Usuarie.find(params[:usuarie]) usuarie = params[:usuarie]
site = usuarie.sites.find(params[:site])
hostname = site.hostname hostname = site.hostname
deploys ||= {} deploys ||= {}

View file

@ -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

View file

@ -2,4 +2,11 @@
class ApplicationRecord < ActiveRecord::Base class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true 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 end

View file

@ -11,7 +11,7 @@ class LogEntry < ApplicationRecord
def resend def resend
return if sent return if sent
ContactJob.perform_later site_id, params[:form], params ContactJob.perform_later site, params[:form], params
end end
def params def params

View file

@ -13,6 +13,8 @@ class Site < ApplicationRecord
include Site::SocialDistributedPress include Site::SocialDistributedPress
include Tienda include Tienda
self.filter_attributes += [/_key/, /_ciphertext\z/]
# Cifrar la llave privada que cifra y decifra campos ocultos. Sutty # Cifrar la llave privada que cifra y decifra campos ocultos. Sutty
# tiene acceso pero los datos se guardan cifrados en el sitio. Esto # tiene acceso pero los datos se guardan cifrados en el sitio. Esto
# protege información privada en repositorios públicos, pero no la # 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 # layouts. Si pasamos un layout que no existe, obtenemos un
# NoMethodError # NoMethodError
@layouts_struct ||= Struct.new(*layout_keys, keyword_init: true) @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, [name.to_sym,
Layout.new(site: self, name: name.to_sym, meta: metadata.delete('meta')&.with_indifferent_access, Layout.new(site: self, name: name.to_sym, meta: metadata.delete('meta')&.with_indifferent_access,
metadata: metadata.with_indifferent_access)] metadata: metadata.with_indifferent_access)]
end.to_h) end)
end end
# TODO: Si la estructura de datos no existe, vamos a producir una # TODO: Si la estructura de datos no existe, vamos a producir una
@ -387,7 +389,7 @@ class Site < ApplicationRecord
end end
def reload def reload
super.tap do |s| super.tap do |_s|
reload_jekyll! reload_jekyll!
end end
self self
@ -477,7 +479,7 @@ class Site < ApplicationRecord
def clone_skel! def clone_skel!
return if jekyll? 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 # Necesita un bloque
repository.rugged.remotes.rename('origin', 'upstream') {} repository.rugged.remotes.rename('origin', 'upstream') {}
@ -577,11 +579,11 @@ class Site < ApplicationRecord
deploy_local = deploys.find_by_type('DeployLocal') deploy_local = deploys.find_by_type('DeployLocal')
deploy_local.git_lfs deploy_local.git_lfs
if !gems_installed? || gemfile_updated? || gemfile_lock_updated? return unless !gems_installed? || gemfile_updated? || gemfile_lock_updated?
deploy_local.bundle
touch deploy_local.bundle
FileUtils.touch(gemfile_path) touch
end FileUtils.touch(gemfile_path)
end end
def gem_path def gem_path

View file

@ -21,6 +21,8 @@ class Usuarie < ApplicationRecord
has_many :blazer_audits, foreign_key: 'user_id', class_name: 'Blazer::Audit' has_many :blazer_audits, foreign_key: 'user_id', class_name: 'Blazer::Audit'
has_many :blazer_queries, foreign_key: 'creator_id', class_name: 'Blazer::Query' has_many :blazer_queries, foreign_key: 'creator_id', class_name: 'Blazer::Query'
self.filter_attributes += [/\Aemail\z/, /\Aencrypted_password\z/]
def name def name
email.split('@', 2).first email.split('@', 2).first
end end
@ -74,10 +76,10 @@ class Usuarie < ApplicationRecord
# Si le usuarie (re)confirma su cuenta con una invitación pendiente, # Si le usuarie (re)confirma su cuenta con una invitación pendiente,
# considerarla aceptada también. # considerarla aceptada también.
def accept_invitation_after_confirmation! def accept_invitation_after_confirmation!
if confirmed? return unless confirmed?
self.invitation_token = nil
self.invitation_accepted_at ||= Time.now.utc self.invitation_token = nil
end self.invitation_accepted_at ||= Time.now.utc
end end
# Muestra un error si el idioma no está disponible al cambiar el # Muestra un error si el idioma no está disponible al cambiar el
@ -85,7 +87,7 @@ class Usuarie < ApplicationRecord
# #
# @return [nil] # @return [nil]
def locale_available! 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')) errors.add(:lang, I18n.t('activerecord.errors.models.usuarie.attributes.lang.not_available'))
nil nil

View file

@ -5,7 +5,7 @@
SiteService = Struct.new(:site, :usuarie, :params, keyword_init: true) do SiteService = Struct.new(:site, :usuarie, :params, keyword_init: true) do
def deploy def deploy
site.enqueue! site.enqueue!
DeployJob.perform_later site.id DeployJob.perform_later site
end end
# Crea un sitio, agrega un rol nuevo y guarda los cambios a la # 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
end end
private def with_all_locales
def with_all_locales(&block)
site.locales.map do |locale| site.locales.map do |locale|
next unless I18n.available_locales.include? locale next unless I18n.available_locales.include? locale

View file

@ -5,4 +5,5 @@
# Configure sensitive parameters which will be filtered from the log file. # Configure sensitive parameters which will be filtered from the log file.
Rails.application.config.filter_parameters += %i[ Rails.application.config.filter_parameters += %i[
password passw secret token _key crypt salt certificate otp ssn key password passw secret token _key crypt salt certificate otp ssn key
_pem _ciphertext email
] ]

View file

@ -3,9 +3,9 @@
namespace :stats do namespace :stats do
desc 'Process stats' desc 'Process stats'
task process_all: :environment do task process_all: :environment do
Site.all.pluck(:id).each do |site_id| Site.all.find_each do |site|
UriCollectionJob.perform_now site_id: site_id, once: true UriCollectionJob.perform_now site: site, once: true
StatCollectionJob.perform_now site_id: site_id, once: true StatCollectionJob.perform_now site: site, once: true
end end
end end
end end