10 Commits

Author SHA1 Message Date
Râu Cao
462dd24da3 WIP contribution nav
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-12 14:32:59 +02:00
Râu Cao
8eb5f093a4 Don't show flash message when opening the root URL while signed out 2023-06-08 08:04:23 +03:00
de45d070aa Merge pull request 'Report Lndhub API errors to Sentry' (#133) from refactor/lndhub_integration into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #133
2023-06-06 15:44:36 +00:00
c0b1112e49 Merge pull request 'Hide unsuccessful outgoing lndhub txs in list' (#132) from bugfix/lndhub_tx_list into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #132
2023-06-06 15:43:38 +00:00
Râu Cao
2f90393eb6 Lndhub v2 service inherits from v1, only adds v2-specific code
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Release Drafter / Update release notes draft (pull_request) Successful in 3s
2023-06-05 13:53:24 +03:00
Râu Cao
8b87072485 Raise custom auth error, re-raise on failed re-auth 2023-06-05 13:52:41 +03:00
Râu Cao
82019f47be Report lndhub errors to Sentry 2023-06-05 13:51:59 +03:00
Râu Cao
259e72167b Hide unsuccessful outgoing lndhub txs in list
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Release Drafter / Update release notes draft (pull_request) Successful in 4s
2023-06-05 13:06:49 +03:00
Râu Cao
7000908891 Auto-login Discourse link
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-04 15:15:09 +03:00
Râu Cao
df0c13b400 Fix potential nil access
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-31 14:43:00 +02:00
14 changed files with 90 additions and 91 deletions

View File

@@ -0,0 +1,3 @@
<%= link_to @path, class: @link_class do %>
<%= @name %>
<% end %>

View File

@@ -0,0 +1,20 @@
# frozen_string_literal: true
class HeaderTabLinkComponent < ViewComponent::Base
def initialize(name:, path:, active: false, disabled: false)
@name = name
@path = path
@active = active
@disabled = disabled
@link_class = class_names_link(path)
end
def class_names_link(path)
common = "block md:inline-block px-5 py-2 rounded-md font-medium text-base md:text-xl"
if @active
"#{common} bg-gray-900/50 text-white"
else
"#{common} text-gray-300 hover:bg-gray-900/30 hover:text-white active:bg-gray-900/30 active:text-white"
end
end
end

View File

@@ -0,0 +1,12 @@
<header class="py-10">
<div class="max-w-6xl md:flex md:gap-x-10 mx-auto px-4 sm:px-6 lg:px-8">
<% if @title.present? %>
<h1 class="text-3xl font-bold text-white">
<%= @title %>
</h1>
<% end %>
<nav class="md:grow flex gap-x-4 <%= @title.present? ? "justify-end" : "justify-start" %>" aria-label="Tabs">
<%= render partial: @tabnav_partial %>
</nav>
</div>
</header>

View File

@@ -0,0 +1,8 @@
# frozen_string_literal: true
class HeaderWithTabsComponent < ViewComponent::Base
def initialize(title: nil, tabnav_partial:)
@title = title
@tabnav_partial = tabnav_partial
end
end

View File

@@ -1,5 +1,5 @@
class DashboardController < ApplicationController class DashboardController < ApplicationController
before_action :authenticate_user! before_action :require_user_signed_in
def index def index
@current_section = :services @current_section = :services

View File

@@ -37,8 +37,8 @@ class Services::LightningController < ApplicationController
session[:ln_auth_token] = auth_token session[:ln_auth_token] = auth_token
@ln_auth_token = auth_token @ln_auth_token = auth_token
end end
rescue rescue => e
# TODO add exception tracking Sentry.capture_exception(e) if Setting.sentry_enabled?
end end
def set_current_section def set_current_section
@@ -49,9 +49,9 @@ class Services::LightningController < ApplicationController
lndhub = Lndhub.new lndhub = Lndhub.new
data = lndhub.balance @ln_auth_token data = lndhub.balance @ln_auth_token
@balance = data["BTC"]["AvailableBalance"] rescue nil @balance = data["BTC"]["AvailableBalance"] rescue nil
rescue rescue AuthError
authenticate_with_lndhub(force_reauth: true) authenticate_with_lndhub(force_reauth: true)
return nil if @fetch_balance_retried raise if @fetch_balance_retried
@fetch_balance_retried = true @fetch_balance_retried = true
fetch_balance fetch_balance
end end
@@ -61,9 +61,9 @@ class Services::LightningController < ApplicationController
txs = lndhub.gettxs @ln_auth_token txs = lndhub.gettxs @ln_auth_token
invoices = lndhub.getuserinvoices(@ln_auth_token).select{|i| i["ispaid"]} invoices = lndhub.getuserinvoices(@ln_auth_token).select{|i| i["ispaid"]}
process_transactions(txs + invoices) process_transactions(txs + invoices)
rescue rescue AuthError
authenticate_with_lndhub(force_reauth: true) authenticate_with_lndhub(force_reauth: true)
return [] if @fetch_transactions_retried raise if @fetch_transactions_retried
@fetch_transactions_retried = true @fetch_transactions_retried = true
fetch_transactions fetch_transactions
end end
@@ -86,6 +86,10 @@ class Services::LightningController < ApplicationController
end end
end end
# Handle an edge case where lndhub.go includes a failed payment in the
# list, which wasn't actually booked
txs.reject!{ |tx| tx["type"] == "paid_invoice" && tx["payment_preimage"].blank? }
txs.sort{ |a,b| b["datetime"] <=> a["datetime"] } txs.sort{ |a,b| b["datetime"] <=> a["datetime"] }
end end
end end

1
app/errors/auth_error.rb Normal file
View File

@@ -0,0 +1 @@
class AuthError < StandardError; end

View File

@@ -12,12 +12,7 @@ class Lndhub
end end
res = Faraday.post "#{@base_url}/#{endpoint}", payload.to_json, headers res = Faraday.post "#{@base_url}/#{endpoint}", payload.to_json, headers
log_error(res) if res.status != 200
if res.status != 200
Rails.logger.error "[lndhub] API request failed:"
Rails.logger.error res.body
#TODO add some kind of exception tracking/notifications
end
JSON.parse(res.body) JSON.parse(res.body)
end end
@@ -31,7 +26,7 @@ class Lndhub
data = JSON.parse(res.body) data = JSON.parse(res.body)
if data.is_a?(Hash) && data["error"] && data["message"] == "bad auth" if data.is_a?(Hash) && data["error"] && data["message"] == "bad auth"
raise "BAD_AUTH" raise AuthError
else else
data data
end end
@@ -68,4 +63,13 @@ class Lndhub
invoice["payment_request"] invoice["payment_request"]
end end
def log_error(res)
Rails.logger.error "[lndhub] API request failed:"
Rails.logger.error res.body
if Setting.sentry_enabled?
Sentry.capture_message("Lndhub API request failed: #{res.body}")
end
end
end end

View File

@@ -1,9 +1,4 @@
class LndhubV2 class LndhubV2 < Lndhub
attr_accessor :auth_token
def initialize
@base_url = ENV["LNDHUB_API_URL"]
end
def post(endpoint, payload, options={}) def post(endpoint, payload, options={})
headers = { "Content-Type" => "application/json" } headers = { "Content-Type" => "application/json" }
@@ -12,64 +7,12 @@ class LndhubV2
elsif options[:admin_token] elsif options[:admin_token]
headers.merge!({ "Authorization" => "Bearer #{options[:admin_token]}" }) headers.merge!({ "Authorization" => "Bearer #{options[:admin_token]}" })
end end
res = Faraday.post "#{@base_url}/#{endpoint}", payload.to_json, headers res = Faraday.post "#{@base_url}/#{endpoint}", payload.to_json, headers
log_error(res) if res.status != 200
if res.status != 200
Rails.logger.error "[lndhub] API request failed:"
Rails.logger.error res.body
#TODO add some kind of exception tracking/notifications
end
JSON.parse(res.body) JSON.parse(res.body)
end end
def get(endpoint, auth_token)
res = Faraday.get("#{@base_url}/#{endpoint}", {}, {
"Content-Type" => "application/json",
"Accept" => "application/json",
"Authorization" => "Bearer #{auth_token}"
})
JSON.parse(res.body)
end
def create(payload)
post "create", payload
end
def authenticate(user)
credentials = post "auth?type=auth", { login: user.ln_account, password: user.ln_password }
self.auth_token = credentials["access_token"]
self.auth_token
end
def balance(user_token=nil)
get "balance", user_token || auth_token
end
def gettxs(user_token)
get "gettxs", user_token || auth_token
end
def getuserinvoices(user_token)
get "getuserinvoices", user_token || auth_token
end
def addinvoice(payload)
invoice = post "addinvoice", {
amt: payload[:amount],
memo: payload[:memo],
description_hash: payload[:description_hash]
}
invoice["payment_request"]
end
#
# V2
#
def create_account(payload={}) def create_account(payload={})
post "v2/users", payload, admin_token: Rails.application.credentials.lndhub[:admin_token] post "v2/users", payload, admin_token: Rails.application.credentials.lndhub[:admin_token]
end end
@@ -78,4 +21,5 @@ class LndhubV2
# Payload: { amount: 1000, description: "", description_hash: "" } # Payload: { amount: 1000, description: "", description_hash: "" }
post "v2/invoices", payload post "v2/invoices", payload
end end
end end

View File

@@ -1,6 +1,10 @@
<%= render HeaderComponent.new(title: "Contributions") %> <%# <%= render HeaderComponent.new(title: "Contributions") %>
<%= render HeaderWithTabsComponent.new(
# title: "Contributions",
tabnav_partial: "shared/tabnav_contributions"
) %>
<%= render MainWithTabnavComponent.new(tabnav_partial: "shared/tabnav_contributions") do %> <%= render MainSimpleComponent.new do %>
<section> <section>
<% if @donations.any? %> <% if @donations.any? %>
<p class="mb-12"> <p class="mb-12">

View File

@@ -1,6 +1,9 @@
<%= render HeaderComponent.new(title: "Contributions") %> <%= render HeaderWithTabsComponent.new(
# title: "Contributions",
tabnav_partial: "shared/tabnav_contributions"
) %>
<%= render MainWithTabnavComponent.new(tabnav_partial: "shared/tabnav_contributions") do %> <%= render MainSimpleComponent.new do %>
<section> <section>
<p class="mb-8"> <p class="mb-8">
Project contributions are how we develop and run all Kosmos software and Project contributions are how we develop and run all Kosmos software and

View File

@@ -21,7 +21,7 @@
<div class="border border-gray-300 rounded-md hover:border-gray-400 <div class="border border-gray-300 rounded-md hover:border-gray-400
bg-[length:95%] bg-center bg-no-repeat bg-[length:95%] bg-center bg-no-repeat
bg-[url(/img/logos/icon_discourse.svg)]"> bg-[url(/img/logos/icon_discourse.svg)]">
<%= link_to "https://community.kosmos.org", <%= link_to "#{Setting.discourse_public_url}/session/sso?return_path=/",
class: "block h-full px-6 py-6 rounded-md" do %> class: "block h-full px-6 py-6 rounded-md" do %>
<h3 class="mb-3.5">Discourse</h3> <h3 class="mb-3.5">Discourse</h3>
<p class="text-gray-600"> <p class="text-gray-600">

View File

@@ -1,6 +1,6 @@
<% <%
# TODO remove when https://github.com/hotwired/turbo/issues/203 is fixed # TODO remove when https://github.com/hotwired/turbo/issues/203 is fixed
enable_turbo = !session[:user_return_to].match?('/discourse/connect') enable_turbo = !session[:user_return_to] || !session[:user_return_to].match?('/discourse/connect')
%> %>
<%= render HeaderCompactComponent.new(title: "Log in") %> <%= render HeaderCompactComponent.new(title: "Log in") %>

View File

@@ -1,12 +1,8 @@
<div class="border-b border-gray-200"> <%= render HeaderTabLinkComponent.new(
<nav class="-mb-px flex" aria-label="Tabs"> name: "Donations", path: contributions_donations_path,
<%= render TabnavLinkComponent.new( active: current_page?(contributions_donations_path)
name: "Donations", path: contributions_donations_path, ) %>
active: current_page?(contributions_donations_path) <%= render HeaderTabLinkComponent.new(
) %> name: "Projects", path: contributions_projects_path,
<%= render TabnavLinkComponent.new( active: current_page?(contributions_projects_path)
name: "Projects", path: contributions_projects_path, ) %>
active: current_page?(contributions_projects_path)
) %>
</nav>
</div>