mirror of
https://github.com/we-promise/sure.git
synced 2026-04-07 06:21:23 +00:00
* Feat(CoinStats): Scaffold implementation, not yet functional * Feat(CoinStats): Implement crypto wallet balance and transactions * Feat(CoinStats): Add tests, Minor improvements * Feat(CoinStats): Utilize bulk fetch API endpoints * Feat(CoinStats): Migrate strings to i8n * Feat(CoinStats): Fix error handling in wallet link modal * Feat(CoinStats): Implement hourly provider sync job * Feat(CoinStats): Generate docstrings * Fix(CoinStats): Validate API Key on provider update * Fix(Providers): Safely handle race condition in merchance creation * Fix(CoinStats): Don't catch system signals in account processor * Fix(CoinStats): Preload before iterating accounts * Fix(CoinStats): Add no opener / referrer to API dashboard link * Fix(CoinStats): Use strict matching for symbols * Fix(CoinStats): Remove dead code in transactions importer * Fix(CoinStats): Avoid transaction fallback ID collisions * Fix(CoinStats): Improve Blockchains fetch error handling * Fix(CoinStats): Enforce NOT NULL constraint for API Key schema * Fix(CoinStats): Migrate sync status strings to i8n * Fix(CoinStats): Use class name rather than hardcoded string * Fix(CoinStats): Use account currency rather than hardcoded USD * Fix(CoinStats): Migrate from standalone to Provider class * Fix(CoinStats): Fix test failures due to string changes
170 lines
5.5 KiB
Ruby
170 lines
5.5 KiB
Ruby
class CoinstatsItemsController < ApplicationController
|
|
before_action :set_coinstats_item, only: [ :show, :edit, :update, :destroy, :sync ]
|
|
|
|
def index
|
|
@coinstats_items = Current.family.coinstats_items.ordered
|
|
end
|
|
|
|
def show
|
|
end
|
|
|
|
def new
|
|
@coinstats_item = Current.family.coinstats_items.build
|
|
@coinstats_items = Current.family.coinstats_items.where.not(api_key: nil)
|
|
@blockchains = fetch_blockchain_options(@coinstats_items.first)
|
|
end
|
|
|
|
def create
|
|
@coinstats_item = Current.family.coinstats_items.build(coinstats_item_params)
|
|
@coinstats_item.name ||= t(".default_name")
|
|
|
|
# Validate API key before saving
|
|
unless validate_api_key(@coinstats_item.api_key)
|
|
return render_error_response(@coinstats_item.errors.full_messages.join(", "))
|
|
end
|
|
|
|
if @coinstats_item.save
|
|
render_success_response(".success")
|
|
else
|
|
render_error_response(@coinstats_item.errors.full_messages.join(", "))
|
|
end
|
|
end
|
|
|
|
def edit
|
|
end
|
|
|
|
def update
|
|
# Validate API key if it's being changed
|
|
unless validate_api_key(coinstats_item_params[:api_key])
|
|
return render_error_response(@coinstats_item.errors.full_messages.join(", "))
|
|
end
|
|
|
|
if @coinstats_item.update(coinstats_item_params)
|
|
render_success_response(".success")
|
|
else
|
|
render_error_response(@coinstats_item.errors.full_messages.join(", "))
|
|
end
|
|
end
|
|
|
|
def destroy
|
|
@coinstats_item.destroy_later
|
|
redirect_to settings_providers_path, notice: t(".success"), status: :see_other
|
|
end
|
|
|
|
def sync
|
|
unless @coinstats_item.syncing?
|
|
@coinstats_item.sync_later
|
|
end
|
|
|
|
respond_to do |format|
|
|
format.html { redirect_back_or_to accounts_path }
|
|
format.json { head :ok }
|
|
end
|
|
end
|
|
|
|
def link_wallet
|
|
coinstats_item_id = params[:coinstats_item_id].presence
|
|
@address = params[:address]&.to_s&.strip.presence
|
|
@blockchain = params[:blockchain]&.to_s&.strip.presence
|
|
|
|
unless coinstats_item_id && @address && @blockchain
|
|
return render_link_wallet_error(t(".missing_params"))
|
|
end
|
|
|
|
@coinstats_item = Current.family.coinstats_items.find(coinstats_item_id)
|
|
|
|
result = CoinstatsItem::WalletLinker.new(@coinstats_item, address: @address, blockchain: @blockchain).link
|
|
|
|
if result.success?
|
|
redirect_to accounts_path, notice: t(".success", count: result.created_count), status: :see_other
|
|
else
|
|
error_msg = result.errors.join("; ").presence || t(".failed")
|
|
render_link_wallet_error(error_msg)
|
|
end
|
|
rescue Provider::Coinstats::Error => e
|
|
render_link_wallet_error(t(".error", message: e.message))
|
|
rescue => e
|
|
Rails.logger.error("CoinStats link wallet error: #{e.class} - #{e.message}")
|
|
render_link_wallet_error(t(".error", message: e.message))
|
|
end
|
|
|
|
private
|
|
|
|
def set_coinstats_item
|
|
@coinstats_item = Current.family.coinstats_items.find(params[:id])
|
|
end
|
|
|
|
def coinstats_item_params
|
|
params.require(:coinstats_item).permit(
|
|
:name,
|
|
:sync_start_date,
|
|
:api_key
|
|
)
|
|
end
|
|
|
|
def validate_api_key(api_key)
|
|
return true if api_key.blank?
|
|
|
|
response = Provider::Coinstats.new(api_key).get_blockchains
|
|
if response.success?
|
|
true
|
|
else
|
|
@coinstats_item.errors.add(:api_key, t("coinstats_items.create.errors.validation_failed", message: response.error&.message))
|
|
false
|
|
end
|
|
rescue => e
|
|
@coinstats_item.errors.add(:api_key, t("coinstats_items.create.errors.validation_failed", message: e.message))
|
|
false
|
|
end
|
|
|
|
def render_error_response(error_message)
|
|
if turbo_frame_request?
|
|
render turbo_stream: turbo_stream.replace(
|
|
"coinstats-providers-panel",
|
|
partial: "settings/providers/coinstats_panel",
|
|
locals: { error_message: error_message }
|
|
), status: :unprocessable_entity
|
|
else
|
|
redirect_to settings_providers_path, alert: error_message, status: :unprocessable_entity
|
|
end
|
|
end
|
|
|
|
def render_success_response(notice_key)
|
|
if turbo_frame_request?
|
|
flash.now[:notice] = t(notice_key, default: notice_key.to_s.humanize)
|
|
@coinstats_items = Current.family.coinstats_items.ordered
|
|
render turbo_stream: [
|
|
turbo_stream.replace(
|
|
"coinstats-providers-panel",
|
|
partial: "settings/providers/coinstats_panel",
|
|
locals: { coinstats_items: @coinstats_items }
|
|
),
|
|
*flash_notification_stream_items
|
|
]
|
|
else
|
|
redirect_to settings_providers_path, notice: t(notice_key), status: :see_other
|
|
end
|
|
end
|
|
|
|
def render_link_wallet_error(error_message)
|
|
@error_message = error_message
|
|
@coinstats_items = Current.family.coinstats_items.where.not(api_key: nil)
|
|
@blockchains = fetch_blockchain_options(@coinstats_items.first)
|
|
render :new, status: :unprocessable_entity
|
|
end
|
|
|
|
def fetch_blockchain_options(coinstats_item)
|
|
return [] unless coinstats_item&.api_key.present?
|
|
|
|
Provider::Coinstats.new(coinstats_item.api_key).blockchain_options
|
|
rescue Provider::Coinstats::Error => e
|
|
Rails.logger.error("CoinStats blockchain fetch failed: item_id=#{coinstats_item.id} error=#{e.class} message=#{e.message}")
|
|
flash.now[:alert] = t("coinstats_items.new.blockchain_fetch_error")
|
|
[]
|
|
rescue StandardError => e
|
|
Rails.logger.error("CoinStats blockchain fetch failed: item_id=#{coinstats_item.id} error=#{e.class} message=#{e.message}")
|
|
flash.now[:alert] = t("coinstats_items.new.blockchain_fetch_error")
|
|
[]
|
|
end
|
|
end
|