Merge pull request 'Add LndHub wallets' (#33) from feature/lndhub into master
Some checks failed
continuous-integration/drone/push Build is failing

Reviewed-on: #33
This commit is contained in:
Râu Cao 2021-11-22 23:04:19 +00:00
commit 25ddab9241
22 changed files with 491 additions and 130 deletions

View File

@ -1 +1,3 @@
EJABBERD_API_URL='https://xmpp.kosmos.org/api'
LNDHUB_API_URL='http://localhost:3023'
LNDHUB_PUBLIC_URL='https://lndhub.kosmos.org'

View File

@ -1 +1,3 @@
EJABBERD_API_URL='https://xmpp.kosmos.org:5443/api'
LNDHUB_API_URL='http://10.1.1.163:3023'
LNDHUB_PUBLIC_URL='https://lndhub.kosmos.org'

2
.gitignore vendored
View File

@ -42,3 +42,5 @@ yarn-debug.log*
# Ignore redis dumps from sidekiq
dump.rdb
/config/credentials/development.key

View File

@ -24,12 +24,18 @@ gem 'bootsnap', '>= 1.4.2', require: false
# Configuration
gem 'dotenv-rails'
# Security
gem 'lockbox'
# Authentication
gem 'warden'
gem 'devise'
gem 'devise_ldap_authenticatable'
gem 'net-ldap'
# Utilities
gem "rqrcode", "~> 2.0"
# HTTP requests
gem 'faraday'

View File

@ -1,84 +1,91 @@
GEM
remote: https://rubygems.org/
specs:
actioncable (6.0.3.4)
actionpack (= 6.0.3.4)
actioncable (6.0.4.1)
actionpack (= 6.0.4.1)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailbox (6.0.3.4)
actionpack (= 6.0.3.4)
activejob (= 6.0.3.4)
activerecord (= 6.0.3.4)
activestorage (= 6.0.3.4)
activesupport (= 6.0.3.4)
actionmailbox (6.0.4.1)
actionpack (= 6.0.4.1)
activejob (= 6.0.4.1)
activerecord (= 6.0.4.1)
activestorage (= 6.0.4.1)
activesupport (= 6.0.4.1)
mail (>= 2.7.1)
actionmailer (6.0.3.4)
actionpack (= 6.0.3.4)
actionview (= 6.0.3.4)
activejob (= 6.0.3.4)
actionmailer (6.0.4.1)
actionpack (= 6.0.4.1)
actionview (= 6.0.4.1)
activejob (= 6.0.4.1)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
actionpack (6.0.3.4)
actionview (= 6.0.3.4)
activesupport (= 6.0.3.4)
actionpack (6.0.4.1)
actionview (= 6.0.4.1)
activesupport (= 6.0.4.1)
rack (~> 2.0, >= 2.0.8)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
actiontext (6.0.3.4)
actionpack (= 6.0.3.4)
activerecord (= 6.0.3.4)
activestorage (= 6.0.3.4)
activesupport (= 6.0.3.4)
actiontext (6.0.4.1)
actionpack (= 6.0.4.1)
activerecord (= 6.0.4.1)
activestorage (= 6.0.4.1)
activesupport (= 6.0.4.1)
nokogiri (>= 1.8.5)
actionview (6.0.3.4)
activesupport (= 6.0.3.4)
actionview (6.0.4.1)
activesupport (= 6.0.4.1)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
activejob (6.0.3.4)
activesupport (= 6.0.3.4)
activejob (6.0.4.1)
activesupport (= 6.0.4.1)
globalid (>= 0.3.6)
activemodel (6.0.3.4)
activesupport (= 6.0.3.4)
activerecord (6.0.3.4)
activemodel (= 6.0.3.4)
activesupport (= 6.0.3.4)
activestorage (6.0.3.4)
actionpack (= 6.0.3.4)
activejob (= 6.0.3.4)
activerecord (= 6.0.3.4)
marcel (~> 0.3.1)
activesupport (6.0.3.4)
activemodel (6.0.4.1)
activesupport (= 6.0.4.1)
activerecord (6.0.4.1)
activemodel (= 6.0.4.1)
activesupport (= 6.0.4.1)
activestorage (6.0.4.1)
actionpack (= 6.0.4.1)
activejob (= 6.0.4.1)
activerecord (= 6.0.4.1)
marcel (~> 1.0.0)
activesupport (6.0.4.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
zeitwerk (~> 2.2, >= 2.2.2)
addressable (2.7.0)
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
bcrypt (3.1.16)
bindex (0.8.1)
bootsnap (1.5.0)
bootsnap (1.9.1)
msgpack (~> 1.0)
builder (3.2.4)
byebug (11.1.3)
capybara (3.33.0)
capybara (3.36.0)
addressable
matrix
mini_mime (>= 0.1.3)
nokogiri (~> 1.8)
rack (>= 1.6.0)
rack-test (>= 0.6.3)
regexp_parser (~> 1.5)
regexp_parser (>= 1.5, < 3.0)
xpath (~> 3.2)
concurrent-ruby (1.1.7)
connection_pool (2.2.3)
crack (0.4.3)
safe_yaml (~> 1.0.0)
chunky_png (1.4.0)
concurrent-ruby (1.1.9)
connection_pool (2.2.5)
crack (0.4.5)
rexml
crass (1.0.6)
database_cleaner (1.8.5)
devise (4.7.3)
database_cleaner (2.0.1)
database_cleaner-active_record (~> 2.0.0)
database_cleaner-active_record (2.0.1)
activerecord (>= 5.a)
database_cleaner-core (~> 2.0.0)
database_cleaner-core (2.0.1)
devise (4.8.0)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0)
@ -88,127 +95,149 @@ GEM
devise (>= 3.4.1)
net-ldap (>= 0.16.0)
diff-lcs (1.4.4)
dotenv (2.7.2)
dotenv-rails (2.7.2)
dotenv (= 2.7.2)
railties (>= 3.2, < 6.1)
dotenv (2.7.6)
dotenv-rails (2.7.6)
dotenv (= 2.7.6)
railties (>= 3.2)
e2mmap (0.1.0)
erubi (1.9.0)
et-orbi (1.2.4)
erubi (1.10.0)
et-orbi (1.2.6)
tzinfo
factory_bot (6.1.0)
factory_bot (6.2.0)
activesupport (>= 5.0.0)
factory_bot_rails (6.1.0)
factory_bot (~> 6.1.0)
factory_bot_rails (6.2.0)
factory_bot (~> 6.2.0)
railties (>= 5.0.0)
faraday (0.17.0)
faraday (1.8.0)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
faraday-httpclient (~> 1.0.1)
faraday-net_http (~> 1.0)
faraday-net_http_persistent (~> 1.1)
faraday-patron (~> 1.0)
faraday-rack (~> 1.0)
multipart-post (>= 1.2, < 3)
ffi (1.13.1)
fugit (1.4.2)
ruby2_keywords (>= 0.0.4)
faraday-em_http (1.0.0)
faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-net_http (1.0.1)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
faraday-rack (1.0.0)
ffi (1.15.4)
fugit (1.5.2)
et-orbi (~> 1.1, >= 1.1.8)
raabro (~> 1.4)
globalid (0.4.2)
activesupport (>= 4.2.0)
hashdiff (0.4.0)
i18n (1.8.5)
globalid (0.5.2)
activesupport (>= 5.0)
hashdiff (1.0.1)
i18n (1.8.11)
concurrent-ruby (~> 1.0)
jbuilder (2.10.1)
jbuilder (2.11.3)
activesupport (>= 5.0.0)
launchy (2.4.3)
addressable (~> 2.3)
launchy (2.5.0)
addressable (~> 2.7)
letter_opener (1.7.0)
launchy (~> 2.2)
letter_opener_web (1.3.4)
letter_opener_web (1.4.1)
actionmailer (>= 3.2)
letter_opener (~> 1.0)
railties (>= 3.2)
listen (3.2.1)
listen (3.7.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
loofah (2.7.0)
lockbox (0.6.6)
loofah (2.12.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.1)
mini_mime (>= 0.1.1)
marcel (0.3.3)
mimemagic (~> 0.3.2)
marcel (1.0.2)
matrix (0.4.2)
method_source (1.0.0)
mimemagic (0.3.5)
mini_mime (1.0.2)
mini_portile2 (2.4.0)
minitest (5.14.2)
msgpack (1.3.3)
mini_mime (1.1.2)
minitest (5.14.4)
msgpack (1.4.2)
multipart-post (2.1.1)
net-ldap (0.16.3)
nio4r (2.5.4)
nokogiri (1.10.10)
mini_portile2 (~> 2.4.0)
net-ldap (0.17.0)
nio4r (2.5.8)
nokogiri (1.12.5-x86_64-linux)
racc (~> 1.4)
orm_adapter (0.5.0)
pg (1.2.3)
public_suffix (4.0.6)
puma (4.3.6)
puma (4.3.10)
nio4r (~> 2.0)
raabro (1.4.0)
racc (1.6.0)
rack (2.2.3)
rack-proxy (0.6.5)
rack-proxy (0.7.0)
rack
rack-test (1.1.0)
rack (>= 1.0, < 3)
rails (6.0.3.4)
actioncable (= 6.0.3.4)
actionmailbox (= 6.0.3.4)
actionmailer (= 6.0.3.4)
actionpack (= 6.0.3.4)
actiontext (= 6.0.3.4)
actionview (= 6.0.3.4)
activejob (= 6.0.3.4)
activemodel (= 6.0.3.4)
activerecord (= 6.0.3.4)
activestorage (= 6.0.3.4)
activesupport (= 6.0.3.4)
rails (6.0.4.1)
actioncable (= 6.0.4.1)
actionmailbox (= 6.0.4.1)
actionmailer (= 6.0.4.1)
actionpack (= 6.0.4.1)
actiontext (= 6.0.4.1)
actionview (= 6.0.4.1)
activejob (= 6.0.4.1)
activemodel (= 6.0.4.1)
activerecord (= 6.0.4.1)
activestorage (= 6.0.4.1)
activesupport (= 6.0.4.1)
bundler (>= 1.3.0)
railties (= 6.0.3.4)
railties (= 6.0.4.1)
sprockets-rails (>= 2.0.0)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
rails-html-sanitizer (1.3.0)
rails-html-sanitizer (1.4.2)
loofah (~> 2.3)
railties (6.0.3.4)
actionpack (= 6.0.3.4)
activesupport (= 6.0.3.4)
railties (6.0.4.1)
actionpack (= 6.0.4.1)
activesupport (= 6.0.4.1)
method_source
rake (>= 0.8.7)
thor (>= 0.20.3, < 2.0)
rake (13.0.1)
rb-fsevent (0.10.4)
rake (13.0.6)
rb-fsevent (0.11.0)
rb-inotify (0.10.1)
ffi (~> 1.0)
redis (4.2.5)
regexp_parser (1.8.2)
redis (4.5.1)
regexp_parser (2.1.1)
responders (3.0.1)
actionpack (>= 5.0)
railties (>= 5.0)
rspec-core (3.10.0)
rexml (3.2.5)
rqrcode (2.1.0)
chunky_png (~> 1.0)
rqrcode_core (~> 1.0)
rqrcode_core (1.2.0)
rspec-core (3.10.1)
rspec-support (~> 3.10.0)
rspec-expectations (3.10.0)
rspec-expectations (3.10.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.10.0)
rspec-mocks (3.10.0)
rspec-mocks (3.10.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.10.0)
rspec-rails (4.0.1)
actionpack (>= 4.2)
activesupport (>= 4.2)
railties (>= 4.2)
rspec-core (~> 3.9)
rspec-expectations (~> 3.9)
rspec-mocks (~> 3.9)
rspec-support (~> 3.9)
rspec-support (3.10.0)
rufus-scheduler (3.7.0)
rspec-rails (5.0.2)
actionpack (>= 5.2)
activesupport (>= 5.2)
railties (>= 5.2)
rspec-core (~> 3.10)
rspec-expectations (~> 3.10)
rspec-mocks (~> 3.10)
rspec-support (~> 3.10)
rspec-support (3.10.3)
ruby2_keywords (0.0.5)
rufus-scheduler (3.8.0)
fugit (~> 1.1, >= 1.1.6)
safe_yaml (1.0.5)
sass-rails (6.0.0)
sassc-rails (~> 2.1, >= 2.1.1)
sassc (2.4.0)
@ -219,11 +248,11 @@ GEM
sprockets (> 3.0)
sprockets-rails
tilt
sidekiq (6.1.3)
sidekiq (6.3.1)
connection_pool (>= 2.2.2)
rack (~> 2.0)
redis (>= 4.2.0)
sidekiq-scheduler (3.0.1)
sidekiq-scheduler (3.1.0)
e2mmap
redis (>= 3, < 5)
rufus-scheduler (~> 3.2)
@ -237,12 +266,12 @@ GEM
sprockets (4.0.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.2.2)
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets-rails (3.4.0)
actionpack (>= 5.2)
activesupport (>= 5.2)
sprockets (>= 3.0.0)
sqlite3 (1.4.2)
thor (1.0.1)
thor (1.1.0)
thread_safe (0.3.6)
thwait (0.2.0)
e2mmap
@ -250,29 +279,29 @@ GEM
turbolinks (5.2.1)
turbolinks-source (~> 5.2)
turbolinks-source (5.2.0)
tzinfo (1.2.7)
tzinfo (1.2.9)
thread_safe (~> 0.1)
warden (1.2.9)
rack (>= 2.0.9)
web-console (4.1.0)
web-console (4.2.0)
actionview (>= 6.0.0)
activemodel (>= 6.0.0)
bindex (>= 0.4.0)
railties (>= 6.0.0)
webmock (3.6.0)
addressable (>= 2.3.6)
webmock (3.14.0)
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
webpacker (4.3.0)
activesupport (>= 4.2)
rack-proxy (>= 0.6.1)
railties (>= 4.2)
websocket-driver (0.7.3)
websocket-driver (0.7.5)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
xpath (3.2.0)
nokogiri (~> 1.8)
zeitwerk (2.4.1)
zeitwerk (2.5.1)
PLATFORMS
ruby
@ -292,10 +321,12 @@ DEPENDENCIES
letter_opener
letter_opener_web
listen (~> 3.2)
lockbox
net-ldap
pg (~> 1.2.3)
puma (~> 4.1)
rails (~> 6.0.3, >= 6.0.3.4)
rqrcode (~> 2.0)
rspec-rails
sass-rails (>= 6)
sidekiq

View File

@ -0,0 +1,57 @@
class LnurlpayController < ApplicationController
before_action :find_user_by_address
def index
render json: {
status: "OK",
callback: "https://accounts.kosmos.org/lnurlpay/#{@user.address}/invoice",
tag: "payRequest",
maxSendable: 1000000,
minSendable: 1000,
metadata: metadata(@user.address),
commentAllowed: 0
}
end
def invoice
amount = params[:amount].to_i # msats
address = params[:address]
validate_amount(amount)
payment_request = @user.ln_create_invoice({
amount: amount,
description_hash: Digest::SHA2.hexdigest(metadata(address))
})
render json: {
status: "OK",
successAction: {
tag: "message",
message: "Payment received. Thanks!"
},
routes: [],
pr: payment_request
}
end
private
def find_user_by_address
address = params[:address].split("@")
@user = User.where(cn: address.first, ou: address.last).first
http_status :not_found if @user.nil?
end
def metadata(address)
"[[\"text/identifier\", \"#{address}\"], [\"text/plain\", \"Sats for #{address}\"]]"
end
def validate_amount(amount)
if amount > 1000000 || amount < 1000
render json: { status: "ERROR", reason: "Invalid amount" }
return
end
end
end

View File

@ -0,0 +1,44 @@
require "rqrcode"
class WalletController < ApplicationController
before_action :require_user_signed_in
before_action :authenticate_with_lndhub
def index
@current_section = :wallet
@wallet_url = "lndhub://#{current_user.ln_login}:#{current_user.ln_password}@#{ENV['LNDHUB_PUBLIC_URL']}"
qrcode = RQRCode::QRCode.new(@wallet_url)
@svg = qrcode.as_svg(
color: "000",
shape_rendering: "crispEdges",
module_size: 6,
standalone: true,
use_path: true
)
@balance = fetch_balance rescue nil
end
private
def authenticate_with_lndhub
if session["ln_auth_token"].present?
@ln_auth_token = session["ln_auth_token"]
else
lndhub = Lndhub.new
auth_token = lndhub.authenticate(current_user)
session["ln_auth_token"] = auth_token
@ln_auth_token = auth_token
end
rescue
# TODO add exception tracking
end
def fetch_balance
lndhub = Lndhub.new
data = lndhub.balance @ln_auth_token
data["BTC"]["AvailableBalance"]
end
end

View File

@ -0,0 +1,2 @@
module LnurlpayHelper
end

View File

@ -0,0 +1,2 @@
module WalletHelper
end

View File

@ -0,0 +1,13 @@
class CreateLndhubWalletJob < ApplicationJob
queue_as :default
def perform(user)
return if user.ln_login.present? && user.ln_password.present?
lndhub = Lndhub.new
credentials = lndhub.create({ partnerid: user.ou, accounttype: "user" })
user.update! ln_login: credentials["login"],
ln_password: credentials["password"]
end
end

View File

@ -10,6 +10,9 @@ class User < ApplicationRecord
validates_uniqueness_of :email
validates :email, email: true
encrypts :ln_login
encrypts :ln_password
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :ldap_authenticatable,
@ -53,4 +56,10 @@ class User < ApplicationRecord
self.valid?
self.errors[attribute_name].blank?
end
def ln_create_invoice(payload)
lndhub = Lndhub.new
lndhub.authenticate self
lndhub.addinvoice payload
end
end

View File

@ -10,6 +10,7 @@ class CreateAccount < ApplicationService
def call
user = create_user_in_database
add_ldap_document
create_lndhub_wallet
if @invitation.present?
update_invitation(user.id)
@ -44,4 +45,8 @@ class CreateAccount < ApplicationService
return if Rails.env.development?
ExchangeXmppContactsJob.perform_later(@invitation.user, @username, @domain)
end
def create_lndhub_wallet
CreateLndhubWalletJob.perform_later(user)
end
end

57
app/services/lndhub.rb Normal file
View File

@ -0,0 +1,57 @@
class Lndhub
attr_accessor :auth_token
def initialize
@base_url = ENV["LNDHUB_API_URL"]
end
def post(endpoint, payload)
headers = { "Content-Type" => "application/json" }
if auth_token
headers.merge!({ "Authorization" => "Bearer #{auth_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
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_login, password: user.ln_password }
self.auth_token = credentials["access_token"]
self.auth_token
end
def balance(user_token)
get "balance", user_token || auth_token
end
def addinvoice(payload)
invoice = post "addinvoice", {
amt: payload[:amount],
description_hash: payload[:description_hash]
}
invoice["payment_request"]
end
end

View File

@ -13,6 +13,10 @@
<%= link_to "Donations", donations_path,
class: @current_section == :contributions ? "active" : nil %>
</li>
<li>
<%= link_to "Wallet", wallet_path,
class: @current_section == :wallet ? "active" : nil %>
</li>
<li>
<%= link_to "Security", security_path,
class: @current_section == :security ? "active" : nil %>

View File

@ -0,0 +1,54 @@
<section class="w-full grid grid-cols-1 md:grid-cols-2">
<div>
<h2>Wallet</h2>
<p>
Send and receive BTC via the Lightning Network.
</p>
</div>
<p class="font-mono mt-4 md:text-right md:mt-0">
<% if @balance %>
<span class="text-xl"><%= @balance %> sats</span><br>
<span class="text-sm text-gray-500">Available balance</span>
<% end %>
</p>
</section>
<section>
<h3>Blue Wallet</h3>
<p>
You can connect
<%= link_to "Blue Wallet", "https://bluewallet.io",
class: "ks-text-link", target: "_blank" %>
(Android or iOS) to your Kosmos lightning wallet. In order to do so,
scan the setup QR code from the Import Wallet screen in the app.
</p>
<p>
<a id="show-setup-code" class="ks-text-link cursor-pointer">Show setup code</a>
<a id="hide-setup-code" class="ks-text-link cursor-pointer" style="display: none;">Hide setup code</a>
</p>
<p id="setup-code" style="display: none;">
<%= raw @svg %>
</p>
</section>
<script type="text/javascript">
let linkShow = document.querySelector('#show-setup-code');
let linkHide = document.querySelector('#hide-setup-code');
let setupCode = document.querySelector('#setup-code');
linkShow.addEventListener('click', function(ev) {
ev.preventDefault();
setupCode.style.display = 'block';
linkShow.style.display = 'none';
linkHide.style.display = 'block';
window.scrollTo(0, document.body.scrollHeight);
});
linkHide.addEventListener('click', function(ev) {
ev.preventDefault();
const el = document.querySelector('#setup-code');
setupCode.style.display = 'none';
linkShow.style.display = 'block';
linkHide.style.display = 'none';
});
</script>

View File

@ -1 +1 @@
LXwVUXfG3P3NMBp56xEQHKY4YABoVvECgctVLuj4XRkCUKQURUC3pt60yBbMfw8Qs7yH1oeNiUv5ouoNrxR35PiquFr05oIPoHbHewB/aRShe7y313q1iXfddfYnom9r7wEETkeuFLBULNrLjJjzlgJgB8A9lmcSKMy9lEdGKJ5LghA6pEGq4KRxlixr8vC8ExcDtdENuxSWPU97cc5YHGjTTaiB9XIM61xtGcBUcREaizSyzloeRjPee71GfWKfVx3XNTQDoKfs6cecWhrSLlOVjsWGoiGlwpff3cPo1pSuYIoNo/YRnfHEr1WTw/+R3nc0wgPMBZSmFLK/8Uunzr8T6Q3vqqWB1QnKBquOcASa8jRaPFBs5k8PUeCT6aYdVBV0GSOGlyll2dBtfwrZgt2ssk24BuqK5pru5vIdhWNR7zl3+R0o2ABLTEcBSuxe754NiPasEKRQ8/K4lojOjnOcwdhdqC7jN2RVS5G03ZgHEqk1m+Z9svtu9PaRAD8I9vrpCQfq9Bifm2qZUSBJrgexHtDnSMfg/36hCUeyd2z0naoq3IWJ/xyCf1S5uzyd5MPh7N76L9FudD5nK39Jrj+yuVayAXQ1M9K89kSCiC2/ZbBE--TCbSksW4umkXZY62--jE6cZdM44o8/AWe7alnotA==
PRI1laGy6XiLpsII0ixFOTCpzzDbrRpIQ7GJ2flvA+UEEiPVQQ4e/pt6ZjumaZ/geTBHCRZklLtS1u6HjucVKHGSR4K/lE1q7u6nfi/wAvwGQdCTJeKf8PetdlsiVZg2OW4Wi2YF7qtb1ipGS/uRlOeLP8JOwp5BNVS1J6tvNX4T5gA7rrB4Cvtfwp1dwLWODHiXbeZK7SN2j7V3NPGZl0LbNg2jquC4DzQOBhMOlwu1IAgOAcqKbHh9tDabpVOcOD5pNSaVaoBO01P8ObbX8x0S0Ug7BPkM5nnyJTQdNH7LHKvtNmrUEk7+fd+VZR6WqEp6SFBoAA7XxvOLy9dIOqvXbAYnrNZX7iI4IpgZWRNIQ7G1qSBjpyPLKC7+nSPmvd4IcgCHVUBdYp0+yYDxLzwuuhPVtyAY4JAIdZY1mhYxx7BzGNiMc3p8AlMS6hPeO9lVQZykViBGnTjR+iah9OQYAihX9FYywtBkNvMXLiYqsVZmv6em9Uk/ivKSIsaFwNwrelCI5H4Q/+/hKFJ1JaMjbYheXCJgcGgfhiC+fIHig/8y8rXd9lWGa1T+PrpA8akcq/K8I6gbxYxpLWcy5Vnz/SyaMurLPVfCq4cD/JfpLSfy0HophaiycZ7D6dr43rnHsPfrt6kveDOgWUK9d7CqHxsWdXbCpJeBoh8/1usIsGsmyhfwHq0dEpRx78bR2EJeM4LOAuQgf69/5rFJydIbXq76l1BftTls1Pgh--OI8c/RQGQ30wT1Ff--M/ltnmdl+FRdlNliTb79lg==

View File

@ -0,0 +1 @@
Ga2G0A==--RCOGf20OFDHTm7qw--PLI8AZQQylfrplQVYg603Q==

View File

@ -1,7 +1,6 @@
require 'sidekiq/web'
Rails.application.routes.draw do
resources :donations
devise_for :users
get 'welcome', to: 'welcome#index'
@ -18,6 +17,13 @@ Rails.application.routes.draw do
resources :invitations, only: ['index', 'show', 'create', 'destroy']
resources :donations
get 'wallet', to: 'wallet#index'
get 'lnurlpay/:address', to: 'lnurlpay#index', constraints: { address: /[^\/]+/}
get 'lnurlpay/:address/invoice', to: 'lnurlpay#invoice', constraints: { address: /[^\/]+/}
namespace :admin do
root to: 'dashboard#index'
get 'invitations', to: 'invitations#index'

View File

@ -0,0 +1,6 @@
class AddLndhubCredentialsToUser < ActiveRecord::Migration[6.0]
def change
add_column :users, :ln_login_ciphertext, :text
add_column :users, :ln_password_ciphertext, :text
end
end

View File

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2020_12_19_121808) do
ActiveRecord::Schema.define(version: 2021_11_20_010540) do
create_table "donations", force: :cascade do |t|
t.integer "user_id"
@ -45,6 +45,8 @@ ActiveRecord::Schema.define(version: 2020_12_19_121808) do
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "unconfirmed_email"
t.text "ln_login_ciphertext"
t.text "ln_password_ciphertext"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end

8
lib/tasks/lndhub.rake Normal file
View File

@ -0,0 +1,8 @@
namespace :lndhub do
desc "Generate wallets for all users"
task :generate_wallets => :environment do |t, args|
User.all.each do |user|
CreateLndhubWalletJob.perform_later(user)
end
end
end

View File

@ -0,0 +1,48 @@
require 'rails_helper'
require 'webmock/rspec'
RSpec.describe CreateLndhubWalletJob, type: :job do
let(:user) { create :user, cn: "willherschel", ou: "kosmos.org" }
subject(:job) { described_class.perform_later(user) }
before do
stub_request(:post, "http://localhost:3023/create")
.to_return(status: 200, headers: {},
body: { login: "abc123", password: "def456" }.to_json)
end
it "creates a new LndHub account" do
perform_enqueued_jobs { job }
expect(WebMock).to have_requested(:post, "http://localhost:3023/create")
.with { |req| req.body == '{"partnerid":"kosmos.org","accounttype":"user"}' }
user.reload
expect(user.ln_login).to eq("abc123")
expect(user.ln_password).to eq("def456")
end
context "with existing credentials stored" do
before do
user.ln_login = "foo"
user.ln_password = "bar"
user.save!
end
it "does not create a new LndHub account" do
perform_enqueued_jobs { job }
expect(WebMock).to_not have_requested(:post, "http://localhost:3023/create")
user.reload
expect(user.ln_login).to eq("foo")
expect(user.ln_password).to eq("bar")
end
end
after do
clear_enqueued_jobs
clear_performed_jobs
end
end