diff --git a/app/models/coinstats_item/syncer.rb b/app/models/coinstats_item/syncer.rb index 459fa0926..76f7343bb 100644 --- a/app/models/coinstats_item/syncer.rb +++ b/app/models/coinstats_item/syncer.rb @@ -12,11 +12,11 @@ class CoinstatsItem::Syncer # @param sync [Sync] Sync record for status tracking def perform_sync(sync) # Phase 1: Import data from CoinStats API - sync.update!(status_text: "Importing wallets from CoinStats...") if sync.respond_to?(:status_text) + sync.update!(status_text: I18n.t("models.coinstats_item.syncer.importing_wallets")) if sync.respond_to?(:status_text) coinstats_item.import_latest_coinstats_data # Phase 2: Check account setup status and collect sync statistics - sync.update!(status_text: "Checking wallet configuration...") if sync.respond_to?(:status_text) + sync.update!(status_text: I18n.t("models.coinstats_item.syncer.checking_configuration")) if sync.respond_to?(:status_text) total_accounts = coinstats_item.coinstats_accounts.count linked_accounts = coinstats_item.coinstats_accounts.joins(:account_provider).joins(:account).merge(Account.visible) @@ -30,18 +30,18 @@ class CoinstatsItem::Syncer if unlinked_accounts.any? coinstats_item.update!(pending_account_setup: true) - sync.update!(status_text: "#{unlinked_accounts.count} wallets need setup...") if sync.respond_to?(:status_text) + sync.update!(status_text: I18n.t("models.coinstats_item.syncer.wallets_need_setup", count: unlinked_accounts.count)) if sync.respond_to?(:status_text) else coinstats_item.update!(pending_account_setup: false) end # Phase 3: Process holdings for linked accounts only if linked_accounts.any? - sync.update!(status_text: "Processing holdings...") if sync.respond_to?(:status_text) + sync.update!(status_text: I18n.t("models.coinstats_item.syncer.processing_holdings")) if sync.respond_to?(:status_text) coinstats_item.process_accounts # Phase 4: Schedule balance calculations for linked accounts - sync.update!(status_text: "Calculating balances...") if sync.respond_to?(:status_text) + sync.update!(status_text: I18n.t("models.coinstats_item.syncer.calculating_balances")) if sync.respond_to?(:status_text) coinstats_item.schedule_account_syncs( parent_sync: sync, window_start_date: sync.window_start_date, diff --git a/app/models/provider/coinstats.rb b/app/models/provider/coinstats.rb index e9d406ab9..0fc0d8695 100644 --- a/app/models/provider/coinstats.rb +++ b/app/models/provider/coinstats.rb @@ -25,6 +25,9 @@ class Provider::Coinstats < Provider res = self.class.get("#{BASE_URL}/wallet/blockchains", headers: auth_headers) handle_response(res) end + rescue SocketError, Net::OpenTimeout, Net::ReadTimeout => e + Rails.logger.error "CoinStats API: GET /wallet/blockchains failed: #{e.class}: #{e.message}" + raise Error, "CoinStats API request failed: #{e.message}" end # Returns blockchain options formatted for select dropdowns @@ -75,6 +78,9 @@ class Provider::Coinstats < Provider ) handle_response(res) end + rescue SocketError, Net::OpenTimeout, Net::ReadTimeout => e + Rails.logger.error "CoinStats API: GET /wallet/balances failed: #{e.class}: #{e.message}" + raise Error, "CoinStats API request failed: #{e.message}" end # Extract balance data for a specific wallet from bulk response @@ -114,6 +120,9 @@ class Provider::Coinstats < Provider ) handle_response(res) end + rescue SocketError, Net::OpenTimeout, Net::ReadTimeout => e + Rails.logger.error "CoinStats API: GET /wallet/transactions failed: #{e.class}: #{e.message}" + raise Error, "CoinStats API request failed: #{e.message}" end # Extract transaction data for a specific wallet from bulk response @@ -162,23 +171,36 @@ class Provider::Coinstats < Provider when 200 JSON.parse(response.body, symbolize_names: true) when 400 - raise Error, "CoinStats: #{response.code} Bad Request - Invalid parameters or request format #{response.body}" + log_api_error(response, "Bad Request") + raise Error, "CoinStats: Invalid request parameters" when 401 - raise Error, "CoinStats: #{response.code} Unauthorized - Invalid or missing API key #{response.body}" + log_api_error(response, "Unauthorized") + raise Error, "CoinStats: Invalid or missing API key" when 403 - raise Error, "CoinStats: #{response.code} Forbidden - #{response.body}" + log_api_error(response, "Forbidden") + raise Error, "CoinStats: Access denied" when 404 - raise Error, "CoinStats: #{response.code} Not Found - Resource not found #{response.body}" + log_api_error(response, "Not Found") + raise Error, "CoinStats: Resource not found" when 409 - raise Error, "CoinStats: #{response.code} Conflict - Resource conflict #{response.body}" + log_api_error(response, "Conflict") + raise Error, "CoinStats: Resource conflict" when 429 - raise Error, "CoinStats: #{response.code} Too Many Requests - Rate limit exceeded #{response.body}" + log_api_error(response, "Too Many Requests") + raise Error, "CoinStats: Rate limit exceeded, try again later" when 500 - raise Error, "CoinStats: #{response.code} Internal Server Error - Server error #{response.body}" + log_api_error(response, "Internal Server Error") + raise Error, "CoinStats: Server error, try again later" when 503 - raise Error, "CoinStats: #{response.code} Service Unavailable - #{response.body}" + log_api_error(response, "Service Unavailable") + raise Error, "CoinStats: Service temporarily unavailable" else - raise Error, "CoinStats: #{response.code} Unexpected Error - #{response.body}" + log_api_error(response, "Unexpected Error") + raise Error, "CoinStats: An unexpected error occurred" end end + + def log_api_error(response, error_type) + Rails.logger.error "CoinStats API: #{response.code} #{error_type} - #{response.body}" + end end diff --git a/config/locales/models/coinstats_item/en.yml b/config/locales/models/coinstats_item/en.yml new file mode 100644 index 000000000..e02b561cf --- /dev/null +++ b/config/locales/models/coinstats_item/en.yml @@ -0,0 +1,10 @@ +--- +en: + models: + coinstats_item: + syncer: + importing_wallets: Importing wallets from CoinStats... + checking_configuration: Checking wallet configuration... + wallets_need_setup: "%{count} wallets need setup..." + processing_holdings: Processing holdings... + calculating_balances: Calculating balances...