diff --git a/app/controllers/notifications_settings_controller.rb b/app/controllers/notifications_settings_controller.rb index 63aa5348..3e867ae8 100644 --- a/app/controllers/notifications_settings_controller.rb +++ b/app/controllers/notifications_settings_controller.rb @@ -4,6 +4,7 @@ class NotificationsSettingsController < ApplicationController before_action :load_bcc_config, only: :index before_action :load_reminder_config, only: :index before_action :load_pending_reminders, only: :index + before_action :load_paperless_status, only: :index authorize_resource :bcc_config, only: :index authorize_resource :reminder_config, only: :index @@ -73,6 +74,12 @@ class NotificationsSettingsController < ApplicationController @pending_reminders.sort_by! { |r| r[:next_at] } end + def load_paperless_status + @paperless_status = Rails.cache.fetch('paperless_ngx_health_check', expires_in: 60.seconds) do + Submissions::UploadToPaperless.health_check + end + end + def email_config_params params.require(:account_config).permit(:key, :value, { value: {} }, { value: [] }).tap do |attrs| attrs[:key] = nil unless attrs[:key].in?([AccountConfig::BCC_EMAILS, AccountConfig::SUBMITTER_REMINDERS]) diff --git a/app/jobs/upload_to_paperless_job.rb b/app/jobs/upload_to_paperless_job.rb index 37847054..a56fbb90 100644 --- a/app/jobs/upload_to_paperless_job.rb +++ b/app/jobs/upload_to_paperless_job.rb @@ -16,7 +16,14 @@ class UploadToPaperlessJob attempt = params['attempt'].to_i - Submissions::UploadToPaperless.call(submission) + Rails.logger.info("[Paperless-ngx] Uploading documents for submission #{submission.id}") + + results = Submissions::UploadToPaperless.call(submission) + + if results + Rails.logger.info("[Paperless-ngx] Upload complete for submission #{submission.id}: " \ + "#{results.size} document(s), task IDs: #{results.join(', ')}") + end rescue Submissions::UploadToPaperless::UploadError, Faraday::Error => e return if attempt >= MAX_ATTEMPTS diff --git a/app/views/notifications_settings/_paperless_status.html.erb b/app/views/notifications_settings/_paperless_status.html.erb new file mode 100644 index 00000000..c30bd230 --- /dev/null +++ b/app/views/notifications_settings/_paperless_status.html.erb @@ -0,0 +1,28 @@ +
+

+ Paperless-ngx +

+
+ <% if !@paperless_status[:configured] %> + Not Configured + + Set PAPERLESS_NGX_URL and PAPERLESS_NGX_TOKEN environment variables to enable. + + <% elsif @paperless_status[:reachable] %> + Connected + + <%= @paperless_status[:url] %> + + <% else %> + Unreachable + + <%= @paperless_status[:url] %> — <%= @paperless_status[:error] %> + + <% end %> +
+ <% if @paperless_status[:configured] %> +

+ Signed documents are automatically uploaded to Paperless-ngx when all parties complete signing. +

+ <% end %> +
diff --git a/app/views/notifications_settings/index.html.erb b/app/views/notifications_settings/index.html.erb index db4bec98..3cc76174 100644 --- a/app/views/notifications_settings/index.html.erb +++ b/app/views/notifications_settings/index.html.erb @@ -21,6 +21,7 @@ <% end %> <%= render 'bcc_form', config: @bcc_config %> + <%= render 'paperless_status' %>

<%= t('sign_request_email_reminders') %> diff --git a/config/initializers/paperless_ngx.rb b/config/initializers/paperless_ngx.rb new file mode 100644 index 00000000..858a0b9c --- /dev/null +++ b/config/initializers/paperless_ngx.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +Rails.application.config.after_initialize do + status = Submissions::UploadToPaperless.health_check + + if !status[:configured] + Rails.logger.info('[Paperless-ngx] Integration not configured (PAPERLESS_NGX_URL / PAPERLESS_NGX_TOKEN not set)') + elsif status[:reachable] + Rails.logger.info("[Paperless-ngx] Connected to #{status[:url]}") + else + Rails.logger.warn("[Paperless-ngx] Configured but unreachable at #{status[:url]}: #{status[:error]}") + end +rescue StandardError => e + Rails.logger.warn("[Paperless-ngx] Health check failed during startup: #{e.message}") +end diff --git a/lib/submissions/upload_to_paperless.rb b/lib/submissions/upload_to_paperless.rb index 6cc0ed83..d8fcbb6b 100644 --- a/lib/submissions/upload_to_paperless.rb +++ b/lib/submissions/upload_to_paperless.rb @@ -26,6 +26,25 @@ module Submissions ENV['PAPERLESS_NGX_URL'].present? && ENV['PAPERLESS_NGX_TOKEN'].present? end + def health_check + return { configured: false, reachable: false, url: nil, error: nil } unless configured? + + url = ENV['PAPERLESS_NGX_URL'] # rubocop:disable Style/FetchEnvVar + response = connection.get('/api/') do |req| + req.headers['Authorization'] = "Token #{ENV['PAPERLESS_NGX_TOKEN']}" # rubocop:disable Style/FetchEnvVar + req.options.timeout = 3 + req.options.open_timeout = 3 + end + + if response.status < 400 + { configured: true, reachable: true, url: url, error: nil } + else + { configured: true, reachable: false, url: url, error: "HTTP #{response.status}" } + end + rescue Faraday::Error => e + { configured: true, reachable: false, url: url, error: e.message } + end + def documents_to_upload(submission, title) documents = [] diff --git a/spec/lib/submissions/upload_to_paperless_spec.rb b/spec/lib/submissions/upload_to_paperless_spec.rb index 1dcd49b4..8ae29803 100644 --- a/spec/lib/submissions/upload_to_paperless_spec.rb +++ b/spec/lib/submissions/upload_to_paperless_spec.rb @@ -205,4 +205,75 @@ RSpec.describe Submissions::UploadToPaperless do end end end + + describe '.health_check' do + context 'when not configured' do + before do + allow(ENV).to receive(:[]).with('PAPERLESS_NGX_URL').and_return(nil) + allow(ENV).to receive(:[]).with('PAPERLESS_NGX_TOKEN').and_return(nil) + end + + it 'returns configured false with no error' do + result = described_class.health_check + + expect(result).to eq(configured: false, reachable: false, url: nil, error: nil) + end + end + + context 'when configured and reachable' do + before do + stub_request(:get, "#{paperless_url}/api/") + .with(headers: { 'Authorization' => "Token #{paperless_token}" }) + .to_return(status: 200, body: '{"version": "2.0"}') + end + + it 'returns configured and reachable with the URL' do + result = described_class.health_check + + expect(result).to eq(configured: true, reachable: true, url: paperless_url, error: nil) + end + end + + context 'when configured but server returns error' do + before do + stub_request(:get, "#{paperless_url}/api/") + .to_return(status: 500, body: 'Internal Server Error') + end + + it 'returns configured but unreachable with HTTP status error' do + result = described_class.health_check + + expect(result).to eq(configured: true, reachable: false, url: paperless_url, error: 'HTTP 500') + end + end + + context 'when configured but connection times out' do + before do + stub_request(:get, "#{paperless_url}/api/") + .to_timeout + end + + it 'returns configured but unreachable with timeout error' do + result = described_class.health_check + + expect(result[:configured]).to be true + expect(result[:reachable]).to be false + expect(result[:url]).to eq(paperless_url) + expect(result[:error]).to be_present + end + end + + context 'when configured but connection refused' do + before do + stub_request(:get, "#{paperless_url}/api/") + .to_raise(Faraday::ConnectionFailed.new('Connection refused')) + end + + it 'returns configured but unreachable with connection error' do + result = described_class.health_check + + expect(result).to eq(configured: true, reachable: false, url: paperless_url, error: 'Connection refused') + end + end + end end diff --git a/spec/system/notifications_settings_spec.rb b/spec/system/notifications_settings_spec.rb index 2edd3eb5..d2f33d73 100644 --- a/spec/system/notifications_settings_spec.rb +++ b/spec/system/notifications_settings_spec.rb @@ -69,6 +69,59 @@ RSpec.describe 'Notifications Settings' do end end + context 'when paperless-ngx is not configured' do + before do + allow(ENV).to receive(:[]).and_call_original + allow(ENV).to receive(:[]).with('PAPERLESS_NGX_URL').and_return(nil) + allow(ENV).to receive(:[]).with('PAPERLESS_NGX_TOKEN').and_return(nil) + end + + it 'shows not configured status' do + visit settings_notifications_path + + expect(page).to have_content('Paperless-ngx') + expect(page).to have_content('Not Configured') + end + end + + context 'when paperless-ngx is configured and reachable' do + before do + allow(ENV).to receive(:[]).and_call_original + allow(ENV).to receive(:[]).with('PAPERLESS_NGX_URL').and_return('http://paperless:8000') + allow(ENV).to receive(:[]).with('PAPERLESS_NGX_TOKEN').and_return('test-token') + + stub_request(:get, 'http://paperless:8000/api/') + .to_return(status: 200, body: '{}') + end + + it 'shows connected status with URL' do + visit settings_notifications_path + + expect(page).to have_content('Paperless-ngx') + expect(page).to have_content('Connected') + expect(page).to have_content('http://paperless:8000') + end + end + + context 'when paperless-ngx is configured but unreachable' do + before do + allow(ENV).to receive(:[]).and_call_original + allow(ENV).to receive(:[]).with('PAPERLESS_NGX_URL').and_return('http://paperless:8000') + allow(ENV).to receive(:[]).with('PAPERLESS_NGX_TOKEN').and_return('test-token') + + stub_request(:get, 'http://paperless:8000/api/') + .to_return(status: 500, body: 'Internal Server Error') + end + + it 'shows unreachable status with error' do + visit settings_notifications_path + + expect(page).to have_content('Paperless-ngx') + expect(page).to have_content('Unreachable') + expect(page).to have_content('HTTP 500') + end + end + context 'when changes sign request email reminders settings' do it 'updates first reminder duration' do visit settings_notifications_path