mirror of
https://github.com/we-promise/sure.git
synced 2026-04-07 06:21:23 +00:00
fix: Enable Banking DNS issues and provide better UI sync feedback (#1021)
* fix(docker): add explicit DNS config to fix enable banking sync * fix(enable-banking): surface sync errors in the UI * fix: add spaces inside array brackets for RuboCop * fix(enable-banking): surface sync errors and partial failures in UI
This commit is contained in:
@@ -237,9 +237,11 @@ class AccountsController < ApplicationController
|
||||
|
||||
# Enable Banking sync stats
|
||||
@enable_banking_sync_stats_map = {}
|
||||
@enable_banking_latest_sync_error_map = {}
|
||||
@enable_banking_items.each do |item|
|
||||
latest_sync = item.syncs.ordered.first
|
||||
@enable_banking_sync_stats_map[item.id] = latest_sync&.sync_stats || {}
|
||||
@enable_banking_latest_sync_error_map[item.id] = latest_sync&.error
|
||||
end
|
||||
|
||||
# CoinStats sync stats
|
||||
|
||||
@@ -15,6 +15,7 @@ module EnableBankingItems
|
||||
@enable_banking_unlinked_count_map ||= {}
|
||||
@enable_banking_duplicate_only_map ||= {}
|
||||
@enable_banking_show_relink_map ||= {}
|
||||
@enable_banking_latest_sync_error_map ||= {}
|
||||
|
||||
# Batch-check if ANY family has manual accounts (same result for all items from same family)
|
||||
family_ids = items.map { |i| i.family_id }.uniq
|
||||
@@ -42,6 +43,7 @@ module EnableBankingItems
|
||||
end
|
||||
stats = (latest_sync&.sync_stats || {})
|
||||
@enable_banking_sync_stats_map[item.id] = stats
|
||||
@enable_banking_latest_sync_error_map[item.id] = latest_sync&.error
|
||||
|
||||
# Whether the family has any manual accounts available to link (from batch query)
|
||||
@enable_banking_has_unlinked_map[item.id] = families_with_manuals.include?(item.family_id)
|
||||
@@ -68,13 +70,6 @@ module EnableBankingItems
|
||||
@enable_banking_show_relink_map[item.id] = false
|
||||
end
|
||||
end
|
||||
|
||||
# Ensure maps are hashes even when items empty
|
||||
@enable_banking_sync_stats_map ||= {}
|
||||
@enable_banking_has_unlinked_map ||= {}
|
||||
@enable_banking_unlinked_count_map ||= {}
|
||||
@enable_banking_duplicate_only_map ||= {}
|
||||
@enable_banking_show_relink_map ||= {}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -18,7 +18,8 @@ class EnableBankingItem::Importer
|
||||
|
||||
session_data = fetch_session_data
|
||||
unless session_data
|
||||
return { success: false, error: "Failed to fetch session data", accounts_updated: 0, transactions_imported: 0 }
|
||||
error_msg = @session_error || "Failed to fetch session data"
|
||||
return { success: false, error: error_msg, accounts_updated: 0, transactions_imported: 0 }
|
||||
end
|
||||
|
||||
# Store raw payload
|
||||
@@ -92,17 +93,41 @@ class EnableBankingItem::Importer
|
||||
end
|
||||
end
|
||||
|
||||
{
|
||||
result = {
|
||||
success: accounts_failed == 0 && transactions_failed == 0,
|
||||
accounts_updated: accounts_updated,
|
||||
accounts_failed: accounts_failed,
|
||||
transactions_imported: transactions_imported,
|
||||
transactions_failed: transactions_failed
|
||||
}
|
||||
if !result[:success] && (accounts_failed > 0 || transactions_failed > 0)
|
||||
parts = []
|
||||
parts << "#{accounts_failed} #{'account'.pluralize(accounts_failed)} failed" if accounts_failed > 0
|
||||
parts << "#{transactions_failed} #{'transaction'.pluralize(transactions_failed)} failed" if transactions_failed > 0
|
||||
result[:error] = parts.join(", ")
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def extract_friendly_error_message(exception)
|
||||
[ exception, exception.cause ].compact.each do |ex|
|
||||
case ex
|
||||
when SocketError then return "DNS resolution failed: check your network/DNS configuration"
|
||||
when Net::OpenTimeout, Net::ReadTimeout then return "Connection timed out: the Enable Banking API may be unreachable"
|
||||
when Errno::ECONNREFUSED then return "Connection refused: the Enable Banking API is unreachable"
|
||||
end
|
||||
end
|
||||
|
||||
msg = exception.message.to_s
|
||||
return "DNS resolution failed: check your network/DNS configuration" if msg.include?("getaddrinfo") || msg.match?(/name or service not known/i)
|
||||
return "Connection timed out: the Enable Banking API may be unreachable" if msg.include?("execution expired") || msg.include?("timeout") || msg.match?(/timed out/i)
|
||||
return "Connection refused: the Enable Banking API is unreachable" if msg.include?("ECONNREFUSED") || msg.match?(/connection refused/i)
|
||||
|
||||
msg
|
||||
end
|
||||
|
||||
def fetch_session_data
|
||||
enable_banking_provider.get_session(session_id: enable_banking_item.session_id)
|
||||
rescue Provider::EnableBanking::EnableBankingError => e
|
||||
@@ -110,9 +135,11 @@ class EnableBankingItem::Importer
|
||||
enable_banking_item.update!(status: :requires_update)
|
||||
end
|
||||
Rails.logger.error "EnableBankingItem::Importer - Enable Banking API error: #{e.message}"
|
||||
@session_error = extract_friendly_error_message(e)
|
||||
nil
|
||||
rescue => e
|
||||
Rails.logger.error "EnableBankingItem::Importer - Unexpected error fetching session: #{e.class} - #{e.message}"
|
||||
@session_error = extract_friendly_error_message(e)
|
||||
nil
|
||||
end
|
||||
|
||||
|
||||
@@ -6,20 +6,34 @@ class EnableBankingItem::SyncCompleteEvent
|
||||
end
|
||||
|
||||
def broadcast
|
||||
enable_banking_item.reload
|
||||
|
||||
# Update UI with latest account data
|
||||
enable_banking_item.accounts.each do |account|
|
||||
account.broadcast_sync_complete
|
||||
end
|
||||
|
||||
# Update the Enable Banking item view
|
||||
family = enable_banking_item.family
|
||||
return unless family
|
||||
|
||||
# Update the Enable Banking item view on the Accounts page
|
||||
enable_banking_item.broadcast_replace_to(
|
||||
enable_banking_item.family,
|
||||
family,
|
||||
target: "enable_banking_item_#{enable_banking_item.id}",
|
||||
partial: "enable_banking_items/enable_banking_item",
|
||||
locals: { enable_banking_item: enable_banking_item }
|
||||
)
|
||||
|
||||
# Update the Settings > Providers panel
|
||||
enable_banking_items = family.enable_banking_items.ordered.includes(:syncs)
|
||||
enable_banking_item.broadcast_replace_to(
|
||||
family,
|
||||
target: "enable_banking-providers-panel",
|
||||
partial: "settings/providers/enable_banking_panel",
|
||||
locals: { enable_banking_items: enable_banking_items }
|
||||
)
|
||||
|
||||
# Let family handle sync notifications
|
||||
enable_banking_item.family.broadcast_sync_complete
|
||||
family.broadcast_sync_complete
|
||||
end
|
||||
end
|
||||
|
||||
@@ -17,6 +17,17 @@ class EnableBankingItem::Syncer
|
||||
sync.update!(status_text: "Importing accounts from Enable Banking...") if sync.respond_to?(:status_text)
|
||||
import_result = enable_banking_item.import_latest_enable_banking_data
|
||||
|
||||
unless import_result[:success]
|
||||
error_msg = import_result[:error]
|
||||
if error_msg.blank? && (import_result[:accounts_failed].to_i > 0 || import_result[:transactions_failed].to_i > 0)
|
||||
parts = []
|
||||
parts << "#{import_result[:accounts_failed]} #{'account'.pluralize(import_result[:accounts_failed])} failed" if import_result[:accounts_failed].to_i > 0
|
||||
parts << "#{import_result[:transactions_failed]} #{'transaction'.pluralize(import_result[:transactions_failed])} failed" if import_result[:transactions_failed].to_i > 0
|
||||
error_msg = parts.join(", ")
|
||||
end
|
||||
raise StandardError.new(error_msg.presence || "Import failed")
|
||||
end
|
||||
|
||||
# Phase 2: Check account setup status and collect sync statistics
|
||||
sync.update!(status_text: "Checking account configuration...") if sync.respond_to?(:status_text)
|
||||
total_accounts = enable_banking_item.enable_banking_accounts.count
|
||||
|
||||
@@ -86,11 +86,20 @@
|
||||
<% end %>
|
||||
|
||||
<%# Sync summary (collapsible) - using shared ProviderSyncSummary component %>
|
||||
<% stats = if defined?(@enable_banking_sync_stats_map) && @enable_banking_sync_stats_map
|
||||
@enable_banking_sync_stats_map[enable_banking_item.id] || {}
|
||||
else
|
||||
enable_banking_item.syncs.ordered.first&.sync_stats || {}
|
||||
end %>
|
||||
<% if defined?(@enable_banking_sync_stats_map) && @enable_banking_sync_stats_map %>
|
||||
<% stats = @enable_banking_sync_stats_map[enable_banking_item.id] || {} %>
|
||||
<% latest_sync_error = defined?(@enable_banking_latest_sync_error_map) && @enable_banking_latest_sync_error_map ? @enable_banking_latest_sync_error_map[enable_banking_item.id] : nil %>
|
||||
<% else %>
|
||||
<% latest_sync = enable_banking_item.syncs.ordered.first %>
|
||||
<% stats = latest_sync&.sync_stats || {} %>
|
||||
<% latest_sync_error = latest_sync&.error %>
|
||||
<% end %>
|
||||
<% if latest_sync_error.present? && stats.is_a?(Hash) %>
|
||||
<% stats = stats.merge(
|
||||
"total_errors" => 1,
|
||||
"errors" => [{ "message" => latest_sync_error }]
|
||||
) %>
|
||||
<% end %>
|
||||
<%= render ProviderSyncSummary.new(
|
||||
stats: stats,
|
||||
provider_item: enable_banking_item
|
||||
|
||||
@@ -112,7 +112,19 @@
|
||||
<% items.each do |item| %>
|
||||
<div class="flex items-center justify-between p-3 rounded-lg bg-container border border-primary">
|
||||
<div class="flex items-center gap-3">
|
||||
<% if item.session_valid? %>
|
||||
<% if item.syncing? %>
|
||||
<div class="w-2 h-2 bg-primary rounded-full animate-pulse"></div>
|
||||
<div>
|
||||
<p class="text-sm font-medium text-primary"><%= item.aspsp_name || t("settings.providers.enable_banking_panel.syncing", default: "Syncing") %></p>
|
||||
<p class="text-xs text-secondary"><%= t("settings.providers.enable_banking_panel.syncing", default: "Syncing") %></p>
|
||||
</div>
|
||||
<% elsif item.sync_error.present? %>
|
||||
<div class="w-2 h-2 bg-destructive rounded-full"></div>
|
||||
<div>
|
||||
<p class="text-sm font-medium text-primary"><%= item.aspsp_name || t("settings.providers.enable_banking_panel.connection_error") %></p>
|
||||
<p class="text-xs text-destructive" title="<%= item.sync_error %>"><%= item.sync_error.truncate(50) %></p>
|
||||
</div>
|
||||
<% elsif item.session_valid? %>
|
||||
<div class="w-2 h-2 bg-success rounded-full"></div>
|
||||
<div>
|
||||
<p class="text-sm font-medium text-primary"><%= item.aspsp_name || "Connected Bank" %></p>
|
||||
|
||||
@@ -115,6 +115,9 @@ services:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
dns:
|
||||
- 8.8.8.8
|
||||
- 1.1.1.1
|
||||
networks:
|
||||
- sure_net
|
||||
|
||||
@@ -129,6 +132,9 @@ services:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
dns:
|
||||
- 8.8.8.8
|
||||
- 1.1.1.1
|
||||
environment:
|
||||
<<: *rails_env
|
||||
networks:
|
||||
|
||||
@@ -59,6 +59,9 @@ services:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
dns:
|
||||
- 8.8.8.8
|
||||
- 1.1.1.1
|
||||
networks:
|
||||
- sure_net
|
||||
|
||||
@@ -73,6 +76,9 @@ services:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
dns:
|
||||
- 8.8.8.8
|
||||
- 1.1.1.1
|
||||
environment:
|
||||
<<: *rails_env
|
||||
networks:
|
||||
|
||||
@@ -172,3 +172,5 @@ en:
|
||||
disconnect_confirm: Are you sure you want to disconnect this Coinbase connection? Your synced accounts will become manual accounts.
|
||||
status_connected: Coinbase is connected and syncing your crypto holdings.
|
||||
status_not_connected: Not connected. Enter your API credentials above to get started.
|
||||
enable_banking_panel:
|
||||
connection_error: Connection Error
|
||||
|
||||
Reference in New Issue
Block a user