mirror of https://github.com/docusealco/docuseal
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
130 lines
3.8 KiB
130 lines
3.8 KiB
# frozen_string_literal: true
|
|
|
|
module Submissions
|
|
module UploadToPaperless
|
|
UploadError = Class.new(StandardError)
|
|
|
|
BOUNDARY_PREFIX = '----DocuSealPaperlessUpload'
|
|
|
|
module_function
|
|
|
|
def call(submission)
|
|
return unless configured?
|
|
|
|
submission.submitters.load unless submission.submitters.loaded?
|
|
|
|
title = build_title(submission)
|
|
created = completed_date(submission)
|
|
results = documents_to_upload(submission, title).map do |attachment, doc_title|
|
|
upload_document(attachment, title: doc_title, created:)
|
|
end
|
|
|
|
results.compact.presence
|
|
end
|
|
|
|
def configured?
|
|
ENV['PAPERLESS_NGX_URL'].present? && ENV['PAPERLESS_NGX_TOKEN'].present?
|
|
end
|
|
|
|
def documents_to_upload(submission, title)
|
|
documents = []
|
|
|
|
if submission.combined_document.attached?
|
|
documents << [submission.combined_document, title]
|
|
else
|
|
submission.submitters.select(&:completed_at?).each do |submitter|
|
|
submitter.documents.each do |doc|
|
|
documents << [doc, title]
|
|
end
|
|
end
|
|
end
|
|
|
|
documents << [submission.audit_trail, "#{title} - Audit Trail"] if submission.audit_trail.attached?
|
|
|
|
documents
|
|
end
|
|
|
|
def build_title(submission)
|
|
submitter_names = submission.submitters
|
|
.select(&:completed_at?)
|
|
.sort_by(&:completed_at)
|
|
.filter_map(&:name)
|
|
.join(', ')
|
|
|
|
template_name = submission.template&.name || 'Document'
|
|
|
|
if submitter_names.present?
|
|
"#{template_name} - #{submitter_names}"
|
|
else
|
|
template_name
|
|
end
|
|
end
|
|
|
|
def completed_date(submission)
|
|
last_completed = submission.submitters.filter_map(&:completed_at).max
|
|
last_completed&.strftime('%Y-%m-%d')
|
|
end
|
|
|
|
def upload_document(attachment, title:, created:)
|
|
blob = attachment.blob
|
|
filename = sanitize_filename(blob.filename.to_s)
|
|
boundary = "#{BOUNDARY_PREFIX}#{SecureRandom.hex(16)}"
|
|
|
|
blob.open do |tempfile|
|
|
body = build_multipart_body(tempfile, filename:, title:, created:, boundary:)
|
|
|
|
response = connection.post('/api/documents/post_document/') do |req|
|
|
req.headers['Authorization'] = "Token #{ENV['PAPERLESS_NGX_TOKEN']}" # rubocop:disable Style/FetchEnvVar
|
|
req.headers['Content-Type'] = "multipart/form-data; boundary=#{boundary}"
|
|
req.body = body
|
|
req.options.read_timeout = 30
|
|
req.options.open_timeout = 10
|
|
end
|
|
|
|
if response.status >= 400
|
|
body_preview = response.body.to_s.truncate(200)
|
|
raise UploadError, "Paperless-ngx upload failed (HTTP #{response.status}): #{body_preview}"
|
|
end
|
|
|
|
response.body.to_s.delete('"').strip
|
|
end
|
|
end
|
|
|
|
def sanitize_filename(filename)
|
|
filename.gsub(/["\r\n\\]/, '_')
|
|
end
|
|
|
|
def build_multipart_body(tempfile, filename:, title:, created:, boundary:)
|
|
parts = []
|
|
|
|
parts << "--#{boundary}\r\n"
|
|
parts << "Content-Disposition: form-data; name=\"document\"; filename=\"#{filename}\"\r\n"
|
|
parts << "Content-Type: application/pdf\r\n\r\n"
|
|
parts << tempfile.read
|
|
parts << "\r\n"
|
|
|
|
parts << "--#{boundary}\r\n"
|
|
parts << "Content-Disposition: form-data; name=\"title\"\r\n\r\n"
|
|
parts << title
|
|
parts << "\r\n"
|
|
|
|
if created
|
|
parts << "--#{boundary}\r\n"
|
|
parts << "Content-Disposition: form-data; name=\"created\"\r\n\r\n"
|
|
parts << created
|
|
parts << "\r\n"
|
|
end
|
|
|
|
parts << "--#{boundary}--\r\n"
|
|
|
|
parts.join
|
|
end
|
|
|
|
def connection
|
|
Faraday.new(url: ENV['PAPERLESS_NGX_URL']) do |f| # rubocop:disable Style/FetchEnvVar
|
|
f.adapter Faraday.default_adapter
|
|
end
|
|
end
|
|
end
|
|
end
|