failed_email.rb 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. class FailedEmail < ApplicationModel
  3. def reprocess(params = {})
  4. ticket, _article, _user, _mail = Channel::EmailParser.new.process_with_timeout(params, data)
  5. raise __('Unknown error: Could not create a ticket from this email.') if !ticket
  6. destroy
  7. ticket
  8. rescue => e
  9. self.retries += 1
  10. self.parsing_error = e
  11. save!
  12. message = "Can't reprocess failed email '#{id}'. This was attempt # #{retries}."
  13. puts "ERROR: #{message}" # rubocop:disable Rails/Output
  14. puts "ERROR: #{e.inspect}" # rubocop:disable Rails/Output
  15. Rails.logger.error message
  16. Rails.logger.error e
  17. false
  18. end
  19. def parsing_error=(input)
  20. message = case input
  21. when StandardError
  22. "#{input.inspect}\n#{input.backtrace&.join("\n")}"
  23. else
  24. input
  25. end
  26. write_attribute :parsing_error, message
  27. end
  28. def self.by_filepath(filepath)
  29. id = Pathname
  30. .new(filepath)
  31. .basename
  32. .to_s
  33. .delete_suffix('.eml')
  34. return if id.include?('.')
  35. find_by(id:)
  36. end
  37. def self.reprocess_all(params = {})
  38. reorder(id: :desc)
  39. .in_batches
  40. .each_record
  41. .select { |elem| elem.reprocess(params) }
  42. .map { |elem| "#{elem.id}.eml" }
  43. end
  44. def self.generate_path
  45. Rails.root.join('tmp', "failed-email-#{SecureRandom.uuid}")
  46. end
  47. def self.export_all(path = generate_path)
  48. in_batches
  49. .each_record
  50. .map { |elem| elem.export(path) }
  51. end
  52. def export(path = self.class.generate_path)
  53. FileUtils.mkdir_p(path)
  54. full_path = path.join("#{id}.eml")
  55. File.binwrite full_path, data
  56. full_path
  57. end
  58. def self.import_all(path)
  59. Dir
  60. .each_child(path)
  61. .filter_map do |filename|
  62. next if !filename.ends_with?('.eml')
  63. import(path.join(filename))
  64. end
  65. end
  66. def self.import(filepath)
  67. failed_email = FailedEmail.by_filepath(filepath.basename)
  68. return if !failed_email
  69. new_data = File.binread filepath
  70. if new_data != failed_email.data
  71. failed_email.data = new_data
  72. failed_email.parsing_error = nil
  73. failed_email.save!
  74. end
  75. return if !failed_email.reprocess
  76. filepath.unlink
  77. filepath
  78. end
  79. end