5
0
Fork 0
mirror of https://0xacab.org/sutty/sutty synced 2024-05-17 02:10:48 +00:00

enviar reportes a gitlab usando la api en lugar del correo

This commit is contained in:
f 2021-05-29 17:42:45 -03:00
parent 691f064a24
commit fc7c2f31dd
9 changed files with 226 additions and 11 deletions

View file

@ -41,6 +41,7 @@ gem 'hiredis'
gem 'image_processing'
gem 'icalendar'
gem 'inline_svg'
gem 'httparty'
gem 'safe_yaml', source: 'https://gems.sutty.nl'
gem 'jekyll', '~> 4.2'
gem 'jekyll-data', source: 'https://gems.sutty.nl'

View file

@ -646,6 +646,7 @@ DEPENDENCIES
haml-lint
hamlit-rails
hiredis
httparty
icalendar
image_processing
inline_svg

View file

@ -40,7 +40,7 @@ class BacktraceJob < ApplicationJob
begin
raise BacktraceException, "#{origin}: #{message}"
rescue BacktraceException => e
ExceptionNotifier.notify_exception(e, data: { site: site.name, params: params, _backtrace: true })
ExceptionNotifier.notify_exception(e, data: { site: site.name, params: params, javascript_backtrace: true })
end
end

View file

@ -0,0 +1,142 @@
# frozen_string_literal: true
class GitlabNotifierJob < ApplicationJob
include ExceptionNotifier::BacktraceCleaner
attr_reader :exception, :options
queue_as :low_priority
def perform(exception, **options)
@exception = exception
@options = options
Rails.logger.info 'Enviando reporte a Gitlab'
i = client.new_issue confidential: true, title: title, description: description
Rails.logger.info "Enviado reporte a Gitlab: #{i['iid']}"
rescue Exception => e
Rails.logger.info 'No entrar en loop'
end
private
# Define si es una excepción de javascript o local
#
# @see BacktraceJob
def javascript?
@javascript ||= options.dig(:data, :javascript_backtrace).present?
end
# @return [String]
def title
@title ||= ''.dup.tap do |t|
t << "[#{exception.class}] " unless javascript?
t << exception.message
end
end
# @return [String]
def description
@description ||= ''.dup.tap do |d|
d << request_section
d << javascript_section
d << javascript_footer
d << backtrace_section
d << data_section
end
end
# @return [String,Nil]
def backtrace
@backtrace ||= exception.backtrace ? clean_backtrace(exception) : nil
end
def env
options[:env]
end
def request
@request ||= ActionDispatch::Request.new(env) if env.present?
end
# @return [GitlabApiClient]
def client
@client ||= GitlabApiClient.new
end
def request_section
return '' unless request
<<~REQUEST
# Request
```
#{request.request_method} #{request.url}#{' '}
#{pp request.filtered_parameters}
```
REQUEST
end
def javascript_section
return '' unless javascript?
options.dig(:data, :params, 'errors')&.map do |error|
<<~JAVASCRIPT
## #{error['type'] || 'NoError'}: #{error['message']}
```
#{Terminal::Table.new headings: error['backtrace'].first.keys, rows: error['backtrace'].map(&:values)}
```
JAVASCRIPT
end&.join
end
def javascript_footer
return '' unless javascript?
<<~JAVASCRIPT
#{options.dig(:data, :params, 'context', 'userAgent')}
<#{options.dig(:data, :params, 'context', 'url')}>
JAVASCRIPT
end
def backtrace_section
return '' if javascript?
return '' unless backtrace
<<~BACKTRACE
## Backtrace
```
#{backtrace.join("\n")}
```
BACKTRACE
end
def data_section
return '' unless options[:data]
<<~DATA
## Data
```
#{pp options[:data]}
```
DATA
end
end

View file

@ -0,0 +1,17 @@
# frozen_string_literal: true
module ExceptionNotifier
# Notifica las excepciones como incidencias en Gitlab
class GitlabNotifier
def initialize(_); end
# Recibe la excepción y empieza la tarea de notificación en segundo
# plano.
#
# @param [Exception]
# @param [Hash]
def call(exception, **options)
GitlabNotifierJob.perform_async(exception, **options)
end
end
end

View file

@ -0,0 +1,61 @@
# frozen_string_literal: true
require 'httparty'
class GitlabApiClient
include HTTParty
# TODO: Hacer configurable por sitio
base_uri ENV.fetch('GITLAB_URI', 'https://0xacab.org')
no_follow true
# Trae todos los proyectos. Como estamos usando un Project Token,
# siempre va a traer uno solo.
#
# @return [HTTParty::Response]
def projects
self.class.get('/api/v4/projects', { query: params(membership: true) })
end
# Obtiene el identificador del proyecto
#
# @return [Integer]
def project_id
@project_id ||= ENV['GITLAB_PROJECT'] || projects&.first&.dig('id')
end
# Crea un issue
#
# @see https://docs.gitlab.com/ee/api/issues.html#new-issue
# @return [HTTParty::Response]
def new_issue(**args)
self.class.post("/api/v4/projects/#{project_id}/issues", { query: params(**args) })
end
# Modifica un issue
#
# @see https://docs.gitlab.com/ee/api/issues.html#edit-issue
# @return [HTTParty::Response]
def edit_issue(**args)
self.class.put("/api/v4/projects/#{project_id}/issues", { query: params(**args) })
end
# Crea un comentario
#
# @see https://docs.gitlab.com/ee/api/notes.html#create-new-issue-note
# @return [HTTParty::Response]
def new_note(iid:, **args)
self.class.post("/api/v4/projects/#{project_id}/issues/#{iid}/notes", { query: params(**args) })
end
private
def params(**args)
default_params.merge(args)
end
# TODO: Que cada sitio tenga su propio token y uri
def default_params
{ private_token: ENV['GITLAB_TOKEN'] }
end
end

View file

@ -1,4 +1,4 @@
<% unless @data[:_backtrace] %>
<% unless @data[:javascript_backtrace] %>
```
<%= raw @backtrace.join("\n") %>
```

View file

@ -1,4 +1,4 @@
<% if @data[:_backtrace] %>
<% if @data[:javascript_backtrace] %>
<% @data.dig(:params, 'errors')&.each do |error| %>
# <%= error['type'] %>: <%= error['message'] %>

View file

@ -147,14 +147,7 @@ Rails.application.configure do
}
config.action_mailer.default_options = { from: ENV.fetch('DEFAULT_FROM', "noreply@sutty.nl") }
config.middleware.use ExceptionNotification::Rack,
error_grouping: true,
email: {
email_prefix: '',
sender_address: ENV.fetch('DEFAULT_FROM', "noreply@sutty.nl"),
exception_recipients: ENV.fetch('EXCEPTION_TO', "errors@sutty.nl"),
normalize_subject: true
}
config.middleware.use ExceptionNotification::Rack, gitlab: {}
Rails.application.routes.default_url_options[:host] = "panel.#{ENV.fetch('SUTTY', 'sutty.nl')}"
Rails.application.routes.default_url_options[:protocol] = 'https'