5
0
Fork 0
mirror of https://0xacab.org/sutty/sutty synced 2024-11-15 02:31:42 +00:00
panel/app/jobs/backtrace_job.rb

98 lines
2.8 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
# Procesa los errores de JavaScript
class BacktraceJob < ApplicationJob
2021-03-19 22:41:03 +00:00
class BacktraceException < RuntimeError; end
queue_as :low_priority
2020-12-07 18:27:24 +00:00
attr_reader :params, :site_id
2020-12-07 18:27:24 +00:00
def perform(site_id:, params:)
@site_id = site_id
2020-12-07 18:27:24 +00:00
@params = params
2021-03-20 16:43:31 +00:00
unless sources.empty?
2021-03-19 23:50:11 +00:00
params['errors'].each do |error|
error['backtrace'].each do |backtrace|
offset = SourceMap::Offset.new(backtrace['line'], backtrace['column'])
mapping = sourcemap.bsearch(offset)
next unless mapping
2021-03-20 16:43:31 +00:00
data = data(backtrace['file'])
2021-03-19 23:50:11 +00:00
backtrace['file'] = mapping.source
backtrace['line'] = mapping.original.line
backtrace['column'] = mapping.original.column
2021-03-20 16:43:31 +00:00
# Encuentra el código fuente del error
source = data.dig('sourcesContent', data['sources']&.index(backtrace['file']))&.split("\n")
backtrace['function'] = source[backtrace['line'] - 1] if source.present?
2021-03-19 23:50:11 +00:00
end
end
end
begin
2021-03-19 22:41:03 +00:00
raise BacktraceException, params['errors']&.first&.dig('message')
rescue BacktraceException => e
2021-03-19 23:50:11 +00:00
ExceptionNotifier.notify_exception(e, data: { site: site.name, params: params, _backtrace: true })
end
end
private
def site
@site ||= Site.find_by_id(site_id)
end
2021-03-20 16:43:31 +00:00
# Obtiene todos los archivos del backtrace solo si los puede descargar
# desde fuentes seguras.
#
# XXX: Idealmente no trabajamos con recursos externos, pero en este
# momento no podemos saber todas las URLs de un sitio y quizás nunca
# lo sabremos :B
def sources
@sources ||= params['errors'].map do |x|
x['backtrace']
end.flatten.map do |x|
x['file'].split('@').last
end.uniq.select do |x|
%r{\Ahttps://} =~ x
end
end
# Descarga y devuelve los datos de un archivo
#
# @param [String] La URL del map
# @return [Hash]
def data(map)
return {} unless map.start_with? 'https://'
2021-03-20 16:43:31 +00:00
map += '.map' unless map.end_with? '.map'
@data ||= {}
@data[map] ||= FastJsonparser.parse(Rails.cache.fetch(map, expires_in: 12.hours) do
Down.open(map).read
end, symbolize_keys: false)
end
# Asume que todos los sourcemaps comparten la misma URL, lo
# correcto sería buscarlo en sourceMappingURL al final de cada
# archivo.
#
# Descarga los archivos y obtiene el backtrace original.
2021-03-20 16:43:31 +00:00
#
# @return [SourceMap::Map]
def sourcemap
@sourcemap ||=
begin
sources.map do |map|
2021-03-20 16:43:31 +00:00
SourceMap::Map.from_hash data(map)
rescue Down::Error, FastJsonparser::Error
SourceMap::Map.new
end.reduce(&:+)
end
end
end