Compare commits
12 Commits
v0.6.0
...
332ad757a5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
332ad757a5
|
||
|
|
07fe8dba71
|
||
|
|
aedaabc7ba
|
||
|
|
8eb5f093a4
|
||
| de45d070aa | |||
| c0b1112e49 | |||
|
|
2f90393eb6
|
||
|
|
8b87072485
|
||
|
|
82019f47be
|
||
|
|
259e72167b
|
||
|
|
7000908891
|
||
|
|
df0c13b400
|
3
Gemfile
3
Gemfile
@@ -58,6 +58,9 @@ gem 'discourse_api'
|
||||
gem "sentry-ruby"
|
||||
gem "sentry-rails"
|
||||
|
||||
# Lightning
|
||||
gem "lnurl"
|
||||
|
||||
group :development, :test do
|
||||
# Use sqlite3 as the database for Active Record
|
||||
gem 'sqlite3', '~> 1.4'
|
||||
|
||||
@@ -180,6 +180,8 @@ GEM
|
||||
listen (3.7.1)
|
||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||
rb-inotify (~> 0.9, >= 0.9.10)
|
||||
lnurl (1.0.1)
|
||||
bech32 (~> 1.1)
|
||||
lockbox (1.1.0)
|
||||
loofah (2.19.0)
|
||||
crass (~> 1.0.2)
|
||||
@@ -409,6 +411,7 @@ DEPENDENCIES
|
||||
letter_opener
|
||||
letter_opener_web
|
||||
listen (~> 3.2)
|
||||
lnurl
|
||||
lockbox
|
||||
net-ldap
|
||||
pagy (~> 6.0, >= 6.0.2)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class DashboardController < ApplicationController
|
||||
before_action :authenticate_user!
|
||||
before_action :require_user_signed_in
|
||||
|
||||
def index
|
||||
@current_section = :services
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
require "rqrcode"
|
||||
require "lnurl"
|
||||
|
||||
class Services::LightningController < ApplicationController
|
||||
before_action :authenticate_user!
|
||||
@@ -8,9 +9,56 @@ class Services::LightningController < ApplicationController
|
||||
|
||||
def index
|
||||
@wallet_url = "lndhub://#{current_user.ln_account}:#{current_user.ln_password}@#{ENV['LNDHUB_PUBLIC_URL']}"
|
||||
initialize_lndhub_qr_code
|
||||
end
|
||||
|
||||
qrcode = RQRCode::QRCode.new(@wallet_url)
|
||||
@svg = qrcode.as_svg(
|
||||
def transactions
|
||||
@transactions = fetch_transactions
|
||||
end
|
||||
|
||||
def qr_lnurlp
|
||||
respond_to do |format|
|
||||
lnurlp_url = "https://kosmos.org/.well-known/lnurlp/#{current_user.cn}"
|
||||
lnurlp_bech32 = Lnurl.new(lnurlp_url).to_bech32
|
||||
qr_code = RQRCode::QRCode.new("lightning:" + lnurlp_bech32)
|
||||
|
||||
format.svg do
|
||||
qr_svg = qr_code.as_svg(
|
||||
color: "000",
|
||||
shape_rendering: "crispEdges",
|
||||
module_size: 6,
|
||||
standalone: true,
|
||||
use_path: true,
|
||||
svg_attributes: {
|
||||
class: 'inline-block'
|
||||
}
|
||||
)
|
||||
send_data(
|
||||
qr_svg,
|
||||
filename: "bitcoin-lightning-#{current_user.address}.svg",
|
||||
type: "image/svg+xml"
|
||||
)
|
||||
end
|
||||
format.png do
|
||||
qr_png = qr_code.as_png(
|
||||
fill: "white",
|
||||
color: "black",
|
||||
size: 1024,
|
||||
)
|
||||
send_data(
|
||||
qr_png,
|
||||
filename: "bitcoin-lightning-#{current_user.address}.png",
|
||||
type: "image/png"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize_lndhub_qr_code
|
||||
qr_code = RQRCode::QRCode.new(@wallet_url)
|
||||
@lndhub_qr_svg = qr_code.as_svg(
|
||||
color: "000",
|
||||
shape_rendering: "crispEdges",
|
||||
module_size: 6,
|
||||
@@ -22,12 +70,6 @@ class Services::LightningController < ApplicationController
|
||||
)
|
||||
end
|
||||
|
||||
def transactions
|
||||
@transactions = fetch_transactions
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def authenticate_with_lndhub(options={})
|
||||
if session[:ln_auth_token].present? && !options[:force_reauth]
|
||||
@ln_auth_token = session[:ln_auth_token]
|
||||
@@ -37,8 +79,8 @@ class Services::LightningController < ApplicationController
|
||||
session[:ln_auth_token] = auth_token
|
||||
@ln_auth_token = auth_token
|
||||
end
|
||||
rescue
|
||||
# TODO add exception tracking
|
||||
rescue => e
|
||||
Sentry.capture_exception(e) if Setting.sentry_enabled?
|
||||
end
|
||||
|
||||
def set_current_section
|
||||
@@ -49,9 +91,9 @@ class Services::LightningController < ApplicationController
|
||||
lndhub = Lndhub.new
|
||||
data = lndhub.balance @ln_auth_token
|
||||
@balance = data["BTC"]["AvailableBalance"] rescue nil
|
||||
rescue
|
||||
rescue AuthError
|
||||
authenticate_with_lndhub(force_reauth: true)
|
||||
return nil if @fetch_balance_retried
|
||||
raise if @fetch_balance_retried
|
||||
@fetch_balance_retried = true
|
||||
fetch_balance
|
||||
end
|
||||
@@ -61,9 +103,9 @@ class Services::LightningController < ApplicationController
|
||||
txs = lndhub.gettxs @ln_auth_token
|
||||
invoices = lndhub.getuserinvoices(@ln_auth_token).select{|i| i["ispaid"]}
|
||||
process_transactions(txs + invoices)
|
||||
rescue
|
||||
rescue AuthError
|
||||
authenticate_with_lndhub(force_reauth: true)
|
||||
return [] if @fetch_transactions_retried
|
||||
raise if @fetch_transactions_retried
|
||||
@fetch_transactions_retried = true
|
||||
fetch_transactions
|
||||
end
|
||||
@@ -86,6 +128,10 @@ class Services::LightningController < ApplicationController
|
||||
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"] }
|
||||
end
|
||||
end
|
||||
|
||||
1
app/errors/auth_error.rb
Normal file
1
app/errors/auth_error.rb
Normal file
@@ -0,0 +1 @@
|
||||
class AuthError < StandardError; end
|
||||
@@ -12,12 +12,7 @@ class Lndhub
|
||||
end
|
||||
|
||||
res = Faraday.post "#{@base_url}/#{endpoint}", payload.to_json, headers
|
||||
|
||||
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
|
||||
log_error(res) if res.status != 200
|
||||
|
||||
JSON.parse(res.body)
|
||||
end
|
||||
@@ -31,7 +26,7 @@ class Lndhub
|
||||
data = JSON.parse(res.body)
|
||||
|
||||
if data.is_a?(Hash) && data["error"] && data["message"] == "bad auth"
|
||||
raise "BAD_AUTH"
|
||||
raise AuthError
|
||||
else
|
||||
data
|
||||
end
|
||||
@@ -68,4 +63,13 @@ class Lndhub
|
||||
|
||||
invoice["payment_request"]
|
||||
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
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
class LndhubV2
|
||||
attr_accessor :auth_token
|
||||
|
||||
def initialize
|
||||
@base_url = ENV["LNDHUB_API_URL"]
|
||||
end
|
||||
class LndhubV2 < Lndhub
|
||||
|
||||
def post(endpoint, payload, options={})
|
||||
headers = { "Content-Type" => "application/json" }
|
||||
@@ -12,64 +7,12 @@ class LndhubV2
|
||||
elsif options[:admin_token]
|
||||
headers.merge!({ "Authorization" => "Bearer #{options[:admin_token]}" })
|
||||
end
|
||||
|
||||
res = Faraday.post "#{@base_url}/#{endpoint}", payload.to_json, headers
|
||||
|
||||
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
|
||||
log_error(res) if res.status != 200
|
||||
|
||||
JSON.parse(res.body)
|
||||
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={})
|
||||
post "v2/users", payload, admin_token: Rails.application.credentials.lndhub[:admin_token]
|
||||
end
|
||||
@@ -78,4 +21,5 @@ class LndhubV2
|
||||
# Payload: { amount: 1000, description: "", description_hash: "" }
|
||||
post "v2/invoices", payload
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<div class="border border-gray-300 rounded-md hover:border-gray-400
|
||||
bg-[length:95%] bg-center bg-no-repeat
|
||||
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 %>
|
||||
<h3 class="mb-3.5">Discourse</h3>
|
||||
<p class="text-gray-600">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<%
|
||||
# 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") %>
|
||||
|
||||
@@ -7,13 +7,25 @@
|
||||
|
||||
<section>
|
||||
<h3>Lightning Address</h3>
|
||||
<p>
|
||||
<p class="mb-6">
|
||||
Your Kosmos user address is also a
|
||||
<a class="ks-text-link" href="https://lightningaddress.com/" target="_blank">Lightning Address</a>!
|
||||
The easiest way to receive sats is by just giving out your address:
|
||||
</p>
|
||||
<p>
|
||||
<strong><%= current_user.address %></strong>
|
||||
<p data-controller="clipboard" class="flex gap-1 sm:w-2/5">
|
||||
<input type="text" id="user_address" class="grow"
|
||||
value=<%= current_user.address %> disabled="disabled"
|
||||
data-clipboard-target="source" />
|
||||
<button id="copy-user-address" class="btn-md btn-icon btn-blue shrink-0"
|
||||
data-clipboard-target="trigger" data-action="clipboard#copy"
|
||||
title="Copy to clipboard">
|
||||
<span class="content-initial">
|
||||
<%= render partial: "icons/copy", locals: { custom_class: "text-white h-4 w-4 inline" } %>
|
||||
</span>
|
||||
<span class="content-active hidden">
|
||||
<%= render partial: "icons/check", locals: { custom_class: "text-white h-4 w-4 inline" } %>
|
||||
</span>
|
||||
</button>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
@@ -39,7 +51,7 @@
|
||||
<button id="hide-setup-code" class="hidden btn-md btn-blue w-full sm:w-auto">Hide setup QR code</button>
|
||||
</p>
|
||||
<p id="setup-code" class="hidden my-10 w-full text-center">
|
||||
<%= raw @svg %>
|
||||
<%= raw @lndhub_qr_svg %>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
@@ -88,6 +100,24 @@
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="mb-12">
|
||||
<h3>QR Code for Donations/Tips</h3>
|
||||
<p>
|
||||
You can print out or publish a QR code for people to scan with their
|
||||
wallet apps, so they can send you sats without a direct personal
|
||||
interaction (for example at a concert, or on your website).
|
||||
</p>
|
||||
<p class="my-6 text-center md:text-left">
|
||||
<%= link_to "Download SVG file",
|
||||
qr_lnurlp_services_lightning_index_path(format: "svg"),
|
||||
class: "btn-md btn-blue w-full sm:w-auto"%>
|
||||
<span class="mx-2 my-2 md:my-0 block md:inline">or</span>
|
||||
<%= link_to "Download PNG file",
|
||||
qr_lnurlp_services_lightning_index_path(format: "png"),
|
||||
class: "btn-md btn-blue w-full sm:w-auto"%>
|
||||
</p>
|
||||
</section>
|
||||
<% end %>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
@@ -24,6 +24,7 @@ Rails.application.routes.draw do
|
||||
resources :lightning, only: [:index] do
|
||||
collection do
|
||||
get 'transactions'
|
||||
get 'qr_lnurlp'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user