Add files via upload

This commit is contained in:
Arc 2019-12-12 23:04:35 +00:00 committed by GitHub
parent 5f3030401d
commit 750dd7dfbf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 2377 additions and 0 deletions

Binary file not shown.

BIN
LNbits/database.sqlite3 Normal file

Binary file not shown.

511
LNbits/server.py Normal file
View File

@ -0,0 +1,511 @@
from flask import Flask, render_template
from flask import Flask, redirect
from flask import request
from flask import jsonify
from flask import Flask, g
from random import seed
from random import random
from flask import json
import re
import os
import sqlite3
import base64
import lnurl
import requests
import hashlib
import time
import json
import bech32
#DATABASE = 'database.db'
INVOICE_KEY = "YOUR-LNTXBOT-INVOICE-KEY" #In the lntxbot bot on telegram type "/api"
ADMIN_KEY = "YOUR-LNTXBOT-ADMIN-KEY"
API_ENDPOINT = "YOUR-LNTXBOT-API-BASE-URL"
app = Flask(__name__)
DEFAULT_PATH = "database.sqlite3"
def db_connect(db_path=DEFAULT_PATH):
con = sqlite3.connect(db_path)
return con
def encrypt_string(hash_string):
sha_signature = \
hashlib.sha256(hash_string.encode()).hexdigest()
return sha_signature
@app.route('/')
def home():
return render_template('index.html')
@app.route('/deletewallet')
def deletewallet():
thewal = request.args.get('wal');
con = db_connect()
cur = con.cursor()
print(thewal)
cur.execute("select * from wallets WHERE hash = '" + str(thewal) + "'")
rowss = cur.fetchall()
if len(rowss) > 0:
cur.close()
print(rowss)
con = db_connect()
cur = con.cursor()
cur.execute("UPDATE wallets SET user = '" + "del" + rowss[0][4] + "' WHERE hash = '" + rowss[0][0] + "'")
con.commit()
cur.close()
con = db_connect()
cur = con.cursor()
cur.execute("UPDATE wallets SET adminkey = '" + "del" + rowss[0][5] + "' WHERE hash = '" + rowss[0][0] + "'")
con.commit()
cur.close()
con = db_connect()
cur = con.cursor()
cur.execute("UPDATE wallets SET inkey = '" + "del" + rowss[0][6] + "' WHERE hash = '" + rowss[0][0] + "'")
con.commit()
cur.close()
con = db_connect()
cur = con.cursor()
print(thewal)
cur.execute("select * from wallets WHERE user = '" + rowss[0][4] + "'")
rowsss = cur.fetchall()
if len(rowsss) > 0:
cur.close()
return render_template('deletewallet.html', theid = rowsss[0][4], thewal = rowsss[0][0])
else:
return render_template('index.html')
else:
return render_template('index.html')
@app.route('/lnurlwallet')
def lnurlwallet():
#put in a function
thestr = request.args.get('lightning');
lnurll = lnurl.decode(thestr)
r = requests.get(url = lnurll)
data = r.json()
callback = data['callback']
maxwithdraw = data['maxWithdrawable']
withdraw = int(maxwithdraw/1000)
k1 = data['k1']
#get invoice
dataj = {'amt': str(withdraw)}
headers = {'Authorization': 'Basic %s' % INVOICE_KEY}
rr = requests.post(url = API_ENDPOINT + "/addinvoice", json = dataj, headers = headers)
dataa = rr.json()
#get callback
pay_req = dataa['pay_req']
payment_hash = dataa['payment_hash']
invurl = callback + '&k1=' + k1 + '&pr=' + pay_req
rrr = requests.get(url = invurl)
dataaa = rrr.json()
print(dataaa)
print("poo")
if dataaa['status'] == "OK":
data = ""
while data == "":
r = requests.post(url = API_ENDPOINT + "/invoicestatus/" + str(payment_hash), headers = headers)
data = r.json()
print(r.json())
adminkey = encrypt_string(payment_hash)[0:20]
inkey = encrypt_string(adminkey)[0:20]
thewal = encrypt_string(inkey)[0:20]
theid = encrypt_string(thewal)[0:20]
thenme = "Bitcoin LN Wallet"
con = db_connect()
cur = con.cursor()
cur.execute("INSERT INTO accounts (userhash) VALUES ('" + theid + "')")
con.commit()
cur.close()
con = db_connect()
cur = con.cursor()
adminkey = encrypt_string(theid)
inkey = encrypt_string(adminkey)
cur.execute("INSERT INTO wallets (hash, balance, transactions, name, user, adminkey, inkey) VALUES ('" + thewal + "',',0," + str(withdraw) + "','0','" + thenme + "','" + theid + "','" + adminkey + "','" + inkey + "')")
con.commit()
cur.close()
con = db_connect()
cur = con.cursor()
print(thewal)
cur.execute("select * from wallets WHERE user = '" + str(theid) + "'")
rows = cur.fetchall()
con.commit()
cur.close()
return render_template('lnurlwallet.html', len = len("1"), walnme = thenme, walbal = str(withdraw), theid = theid, thewal = thewal, adminkey = adminkey, inkey = inkey)
else:
return render_template('index.html')
@app.route('/wallet')
def wallet():
theid = request.args.get('usr');
thewal = request.args.get('wal');
theamt = request.args.get('amt');
thenme = request.args.get('nme');
if not thewal:
return render_template('index.html')
else:
#Checks if the user exists in "accounts"
con = db_connect()
cur = con.cursor()
print(thewal)
cur.execute("select * from accounts WHERE userhash = '" + str(theid) + "'")
rows = cur.fetchall()
if len(rows) > 0:
cur.close()
#Yes, check the user has a wallet
con = db_connect()
cur = con.cursor()
print(thewal)
cur.execute("select * from wallets WHERE user = '" + str(theid) + "'")
rowss = cur.fetchall()
if len(rowss) > 0:
cur.close()
#Checks if the current wallet exists
con = db_connect()
cur = con.cursor()
print(thewal)
cur.execute("select * from wallets WHERE hash = '" + str(thewal) + "'")
rowsss = cur.fetchall()
if len(rowsss) > 0:
cur.close()
walb = rowsss[0][1].split(",")[-1]
return render_template('wallet.html', thearr = rowss, len = len(rowss), walnme = rowsss[0][3], user = theid, walbal = walb, theid = theid, thewal = thewal, transactions = rowsss[0][2], adminkey = rowsss[0][5], inkey = rowsss[0][6])
else:
cur.close()
con = db_connect()
cur = con.cursor()
adminkey = encrypt_string(thewal)
inkey = encrypt_string(adminkey)
cur.execute("INSERT INTO wallets (hash, balance, transactions, name, user, adminkey, inkey) VALUES ('" + thewal + "',',0','0','" + thenme + "','" + theid + "','" + adminkey + "','" + inkey + "')")
con.commit()
cur.close()
con = db_connect()
cur = con.cursor()
print(thewal)
cur.execute("select * from wallets WHERE user = '" + str(theid) + "'")
rowss = cur.fetchall()
cur.close()
return render_template('wallet.html', thearr = rowss, len = len(rowss), walnme = thenme, walbal = '0', theid = theid, thewal = thewal, adminkey = adminkey, inkey = inkey)
else:
cur.close()
con = db_connect()
cur = con.cursor()
adminkey = encrypt_string(theid)
inkey = encrypt_string(adminkey)
cur.execute("INSERT INTO wallets (hash, balance, transactions, name, user, adminkey, inkey) VALUES ('" + thewal + "',',0','0','" + thenme + "','" + theid + "','" + adminkey + "','" + inkey + "')")
con.commit()
cur.close()
return render_template('wallet.html', len = len("1"), walnme = thenme, walbal = '0', theid = theid, thewal = thewal, adminkey = adminkey, inkey = inkey)
else:
cur.close()
con = db_connect()
cur = con.cursor()
cur.execute("INSERT INTO accounts (userhash) VALUES ('" + theid + "')")
con.commit()
cur.close()
con = db_connect()
cur = con.cursor()
adminkey = encrypt_string(theid)
inkey = encrypt_string(adminkey)
cur.execute("INSERT INTO wallets (hash, balance, transactions, name, user, adminkey, inkey) VALUES ('" + thewal + "',',0','0','" + thenme + "','" + theid + "','" + adminkey + "','" + inkey + "')")
con.commit()
cur.close()
con = db_connect()
cur = con.cursor()
print(thewal)
cur.execute("select * from wallets WHERE user = '" + str(theid) + "'")
rows = cur.fetchall()
con.commit()
cur.close()
return render_template('wallet.html', len = len("1"), walnme = thenme, walbal = '0', theid = theid, thewal = thewal, adminkey = adminkey, inkey = inkey)
#API requests
@app.route('/v1/invoices', methods=['GET', 'POST'])
def api_invoices():
if request.headers['Content-Type'] == 'application/json':
postedjson = request.json
print(postedjson)
if "value" in postedjson:
if postedjson["value"].isdigit() == True:
if "memo" in postedjson:
con = db_connect()
cur = con.cursor()
cur.execute("select * from wallets WHERE inkey = '" + request.headers['Grpc-Metadata-macaroon']+ "'")
rows = cur.fetchall()
if len(rows) > 0:
cur.close()
dataj = {'amt': postedjson["value"], 'memo': postedjson["memo"]}
headers = {'Authorization': 'Basic %s' % INVOICE_KEY}
r = requests.post(url = API_ENDPOINT + "/addinvoice", json = dataj, headers = headers)
data = r.json()
pay_req = data['pay_req']
payment_hash = data['payment_hash']
con = db_connect()
cur = con.cursor()
cur.execute("INSERT INTO apipayments (payhash, amount, wallet, paid, inkey, memo) VALUES ('" + payment_hash + "','" + postedjson["value"] + "','" + rows[0][0] + "','0','" + request.headers['Grpc-Metadata-macaroon'] + "','" + postedjson["memo"] + "')")
con.commit()
cur.close()
return jsonify({"pay_req": pay_req, "payment_hash": payment_hash}), 200
else:
return jsonify({"ERROR": "NO KEY"}), 200
else:
return jsonify({"ERROR": "NO MEMO"}), 200
else:
return jsonify({"ERROR": "VALUE MUST BE A NUMMBER"}), 200
else:
return jsonify({"ERROR": "NO VALUE"}), 200
else:
return jsonify({"ERROR": "MUST BE JSON"}), 200
#API requests
@app.route('/v1/channels/transactions', methods=['GET', 'POST'])
def api_transactions():
if request.headers['Content-Type'] == 'application/json':
postedjson = request.json
print(postedjson)
print(postedjson["payment_request"])
if "payment_request" in postedjson:
con = db_connect()
cur = con.cursor()
print(request.headers['Grpc-Metadata-macaroon'])
print()
cur.execute("select * from wallets WHERE adminkey = '" + request.headers['Grpc-Metadata-macaroon']+ "'")
rows = cur.fetchall()
if len(rows) > 0:
cur.close()
s = postedjson["payment_request"]
result = re.search('lnbc(.*)1p', s)
tempp = result.group(1)
alpha = ""
num = ""
for i in range(len(tempp)):
if (tempp[i].isdigit()):
num = num+ tempp[i]
else:
alpha += tempp[i]
sats = ""
if alpha == "n":
sats = int(num)/10
elif alpha == "u":
sats = int(num)*100
elif alpha == "m":
sats = int(num)*100000
print(sats)
print(alpha)
print(num)
dataj = {'invoice': postedjson["payment_request"]}
headers = {'Authorization': 'Basic %s' % ADMIN_KEY}
r = requests.post(url = API_ENDPOINT + "/payinvoice", json = dataj, headers = headers)
data = r.json()
print(data);
con = db_connect()
cur = con.cursor()
cur.execute("INSERT INTO apipayments (payhash, amount, wallet, paid, adminkey, memo) VALUES ('" + data["decoded"]["payment_hash"] + "','" + str(-int(data["decoded"]["num_satoshis"])) + "','" + rows[0][0] + "','1','" + request.headers['Grpc-Metadata-macaroon'] + "','" + data["decoded"]["description"] + "')")
con.commit()
cur.close()
con = db_connect()
cur = con.cursor()
cur.execute("select * from apipayments WHERE payhash = '" + data["decoded"]["payment_hash"] + "'")
rowss = cur.fetchall()
cur.close()
data["decoded"]["num_satoshis"]
lastamt = rows[0][1].split(",")
newamt = int(lastamt[-1]) - int(data["decoded"]["num_satoshis"])
updamt = rows[0][1] + "," + str(newamt)
thetime = time.time()
transactions = rows[0][2] + "!" + rowss[0][5] + "," + str(thetime) + "," + str(rowss[0][1]) + "," + str(newamt)
con = db_connect()
cur = con.cursor()
cur.execute("UPDATE wallets SET balance = '" + updamt + "', transactions = '" + transactions + "' WHERE hash = '" + rows[0][0] + "'")
con.commit()
cur.close()
return jsonify({"PAID": "TRUE"}), 200
else:
return jsonify({"ERROR": "BAD AUTH"}), 200
return jsonify({"ERROR": "NO PAY REQ"}), 200
return jsonify({"ERROR": "MUST BE JSON"}), 200
@app.route('/v1/invoice/<payhash>', methods=['GET'])
def api_checkinvoice(payhash):
if request.headers['Content-Type'] == 'application/json':
print(request.headers["Grpc-Metadata-macaroon"])
con = db_connect()
cur = con.cursor()
cur.execute("select * from apipayments WHERE payhash = '" + payhash + "'")
rows = cur.fetchall()
cur.close()
print(payhash)
if request.headers["Grpc-Metadata-macaroon"] == rows[0][4]:
if rows[0][3] == "0":
print(rows[0][3])
print("did it work?")
headers = {'Authorization': 'Basic %s' % INVOICE_KEY}
r = requests.post(url = API_ENDPOINT + "/invoicestatus/" + payhash, headers = headers)
data = r.json()
print(r.json())
print("no")
if data == "":
return jsonify({"PAID": "FALSE"}), 400
else:
con = db_connect()
cur = con.cursor()
cur.execute("select * from wallets WHERE hash = '" + rows[0][2] + "'")
rowsss = cur.fetchall()
con.commit()
cur.close()
lastamt = rowsss[0][1].split(",")
newamt = int(lastamt[-1]) + int(rows[0][1])
updamt = rowsss[0][1] + "," + str(newamt)
thetime = time.time()
transactions = rowsss[0][2] + "!" + rows[0][5] + "," + str(thetime) + "," + str(rows[0][1]) + "," + str(newamt)
con = db_connect()
cur = con.cursor()
cur.execute("UPDATE wallets SET balance = '" + updamt + "', transactions = '" + transactions + "' WHERE hash = '" + rows[0][2] + "'")
con.commit()
cur.close()
con = db_connect()
cur = con.cursor()
cur.execute("UPDATE apipayments SET paid = '1' WHERE payhash = '" + payhash + "'")
con.commit()
cur.close()
return jsonify({"PAID": "TRUE"}), 200
else:
return jsonify({"PAID": "TRUE"}), 200
else:
return jsonify({"ERROR": "WRONG KEY"}), 400
else:
return jsonify({"ERROR": "NEEDS TO BE JSON"}), 400
if __name__ == '__main__':
app.run(debug=True, host= '0.0.0.0')

View File

@ -0,0 +1,199 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>LNBits Wallet</title>
<meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name='viewport'>
<!-- Bootstrap 3.3.2 -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='bootstrap/css/bootstrap.min.css') }}">
<!-- FontAwesome 4.3.0 -->
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet" type="text/css" />
<!-- Ionicons 2.0.0 -->
<link href="http://code.ionicframework.com/ionicons/2.0.0/css/ionicons.min.css" rel="stylesheet" type="text/css" />
<!-- Theme style -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='dist/css/AdminLTE.min.css') }}">
<!-- AdminLTE Skins. Choose a skin from the css/skins
folder instead of downloading all of them to reduce the load. -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='dist/css/skins/_all-skins.min.css') }}">
<!-- iCheck -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/iCheck/flat/blue.css') }}">
<!-- Morris chart -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/morris/morris.css') }}">
<!-- jvectormap -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/jvectormap/jquery-jvectormap-1.2.2.css') }}">
<!-- Date Picker -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/datepicker/datepicker3.css') }}">
<!-- Daterange picker -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/daterangepicker/daterangepicker-bs3.css') }}">
<!-- bootstrap wysihtml5 - text editor -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.min.css') }}">
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
<![endif]-->
<style>
.small-box>.small-box-footer {
text-align: left;
padding-left: 10px;
}
</style>
</head>
<body class="skin-blue">
<div class="wrapper">
<header class="main-header">
<!-- Logo -->
<a href="index2.html" class="logo"><b>LN</b>bits</a></a>
<!-- Header Navbar: style can be found in header.less -->
<nav class="navbar navbar-static-top" role="navigation">
<!-- Sidebar toggle button-->
<a href="#" class="sidebar-toggle" data-toggle="offcanvas" role="button">
<span class="sr-only">Toggle navigation</span>
</a>
<div class="navbar-custom-menu">
<ul class="nav navbar-nav">
<!-- Messages: style can be found in dropdown.less-->
<li class="dropdown messages-menu">
</li>
</ul>
</div>
</nav>
</header>
<!-- Left side column. contains the logo and sidebar -->
<aside class="main-sidebar">
<!-- sidebar: style can be found in sidebar.less -->
<section class="sidebar">
<!-- Sidebar user panel -->
<!-- /.search form -->
<!-- sidebar menu: : style can be found in sidebar.less -->
<ul class="sidebar-menu">
<li class="header">MAIN NAVIGATION</li>
<li class="active treeview">
<a href="#">
<i class="fa fa-th"></i> <span>Wallets</span>
<i class="fa fa-angle-left pull-right"></i>
</a>
<ul class="treeview-menu">
<li class="active"><a href="wallet?wal={{thewal}}" >
<i class="fa fa-circle-o"></i> {{ walnme }}</a></li>
</ul>
</li>
</section>
<!-- /.sidebar -->
</aside>
<!-- Right side column. Contains the navbar and content of the page -->
<div class="content-wrapper">
<!-- Content Header (Page header) -->
<section class="content-header">
<h1>
Wallet
<small>Control panel</small>
</h1>
<ol class="breadcrumb">
<li><a href="#"><i class="fa fa-dashboard"></i> Home</a></li>
<li class="active">Wallets</li>
<li href="wallet?wal={{thewal}}" class="active">{{ walnme }}</li>
</ol>
</section>
<!-- Main content -->
<section class="content">
<h1>Wallet Deleted!
</section>
</div><!-- /.content-wrapper -->
<footer class="main-footer">
<div class="pull-right hidden-xs">
<b>BETA</b>
</div>
<strong>Learn more about FOSSAW <a href="https://github.com/arcbtc/FOSSAW">https://github.com/arcbtc/FOSSAW</a>.</strong>
</footer>
</div><!-- ./wrapper -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.min.css') }}">
<!-- jQuery 2.1.3 -->
<script src="{{ url_for('static', filename='plugins/jQuery/jQuery-2.1.3.min.js') }}"></script>
<!-- jQuery UI 1.11.2 -->
<script src="http://code.jquery.com/ui/1.11.2/jquery-ui.min.js" type="text/javascript"></script>
<!-- Resolve conflict in jQuery UI tooltip with Bootstrap tooltip -->
<script>
$.widget.bridge('uibutton', $.ui.button);
</script>
<!-- Bootstrap 3.3.2 JS -->
<script src="{{ url_for('static', filename='bootstrap/js/bootstrap.min.js') }}" type="text/javascript"></script>
<!-- Morris.js charts -->
<script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
<script src="{{ url_for('static', filename='plugins/morris/morris.min.js') }}" type="text/javascript"></script>
<!-- Sparkline -->
<script src="{{ url_for('static', filename='plugins/sparkline/jquery.sparkline.min.js') }}" type="text/javascript"></script>
<!-- jvectormap -->
<script src="{{ url_for('static', filename='plugins/jvectormap/jquery-jvectormap-1.2.2.min.js') }}" type="text/javascript"></script>
<script src="{{ url_for('static', filename='plugins/jvectormap/jquery-jvectormap-world-mill-en.js') }}" type="text/javascript"></script>
<!-- jQuery Knob Chart -->
<script src="{{ url_for('static', filename='plugins/knob/jquery.knob.js') }}" type="text/javascript"></script>
<!-- daterangepicker -->
<script src="{{ url_for('static', filename='plugins/daterangepicker/daterangepicker.js') }}" type="text/javascript"></script>
<!-- datepicker -->
<script src="{{ url_for('static', filename='plugins/datepicker/bootstrap-datepicker.js') }}" type="text/javascript"></script>
<!-- Bootstrap WYSIHTML5 -->
<script src="{{ url_for('static', filename='plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.all.min.js') }}" type="text/javascript"></script>
<!-- iCheck -->
<script src="{{ url_for('static', filename='plugins/iCheck/icheck.min.js') }}" type="text/javascript"></script>
<!-- Slimscroll -->
<script src="{{ url_for('static', filename='plugins/slimScroll/jquery.slimscroll.min.js') }}" type="text/javascript"></script>
<!-- FastClick -->
<script src="{{ url_for('static', filename='plugins/fastclick/fastclick.min.js') }}"></script>
<!-- AdminLTE App -->
<script src="{{ url_for('static', filename='dist/js/app.min.js') }}" type="text/javascript"></script>
<!-- AdminLTE dashboard demo (This is only for demo purposes) -->
<script src="{{ url_for('static', filename='dist/js/pages/dashboard.js') }}" type="text/javascript"></script>
<!-- AdminLTE for demo purposes -->
<script src="{{ url_for('static', filename='dist/js/demo.js') }}" type="text/javascript"></script>
<script>
window.location.href = "wallet?usr={{ theid }}&wal={{ thewal }}"; //will redirect to your blog page (an ex: blog.html)
</script>
</body>
</html>

249
LNbits/templates/index.html Normal file
View File

@ -0,0 +1,249 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>LNBits Wallet</title>
<meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name='viewport'>
<!-- Bootstrap 3.3.2 -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='bootstrap/css/bootstrap.min.css') }}">
<!-- FontAwesome 4.3.0 -->
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet" type="text/css" />
<!-- Ionicons 2.0.0 -->
<link href="http://code.ionicframework.com/ionicons/2.0.0/css/ionicons.min.css" rel="stylesheet" type="text/css" />
<!-- Theme style -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='dist/css/AdminLTE.min.css') }}">
<!-- AdminLTE Skins. Choose a skin from the css/skins
folder instead of downloading all of them to reduce the load. -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='dist/css/skins/_all-skins.min.css') }}">
<!-- iCheck -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/iCheck/flat/blue.css') }}">
<!-- Morris chart -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/morris/morris.css') }}">
<!-- jvectormap -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/jvectormap/jquery-jvectormap-1.2.2.css') }}">
<!-- Date Picker -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/datepicker/datepicker3.css') }}">
<!-- Daterange picker -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/daterangepicker/daterangepicker-bs3.css') }}">
<!-- bootstrap wysihtml5 - text editor -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.min.css') }}">
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
<![endif]-->
<style>
.small-box>.small-box-footer {
text-align: left;
padding-left: 10px;
}
</style>
</head>
<body class="skin-blue">
<div class="wrapper">
<header class="main-header">
<!-- Logo -->
<a href="index.html" class="logo"><b>LN</b>bits</a>
<!-- Header Navbar: style can be found in header.less -->
<nav class="navbar navbar-static-top" role="navigation">
<!-- Sidebar toggle button-->
<a href="#" class="sidebar-toggle" data-toggle="offcanvas" role="button">
<span class="sr-only">Toggle navigation</span>
</a>
<div class="navbar-custom-menu">
<ul class="nav navbar-nav">
<!-- Messages: style can be found in dropdown.less-->
<li class="dropdown messages-menu">
</li>
</ul>
</div>
</nav>
</header>
<!-- Left side column. contains the logo and sidebar -->
<aside class="main-sidebar">
<!-- sidebar: style can be found in sidebar.less -->
<section class="sidebar">
<!-- Sidebar user panel -->
<!-- /.search form -->
<!-- sidebar menu: : style can be found in sidebar.less -->
<ul class="sidebar-menu">
<li class="header">MAIN NAVIGATION</li>
<li><a href="../documentation/index.html"><i class="fa fa-book"></i> Home</a></li>
</ul>
</section>
<!-- /.sidebar -->
</aside>
<!-- Right side column. Contains the navbar and content of the page -->
<div class="content-wrapper">
<!-- Content Header (Page header) -->
<section class="content-header">
<ol class="breadcrumb">
<li><a href="/index.html"><i class="fa fa-dashboard"></i> Home</a></li>
</ol><br/><br/>
<div class="alert alert-danger alert-dismissable">
<h4>Warning - Wallet is still in BETA and very, very #reckless, please be careful with your funds!</h4>
</div>
</section>
<!-- Main content -->
<section class="content">
<div class="row">
<div class="col-md-4">
<!-- Default box -->
<div class="box">
<div class="box-header">
<h1>
<a href="index.html" class="logo"><b>LN</b>bits</a>
<small>free and open-source lightning wallet</small>
</h1>
<p>
LNbits is a simple, free and open-source lightning-network wallet for bits and bobs. You can run it on your own server, or use this one.
<br/><br/>
The wallet can be used in a variety of ways, an instant wallet for LN demonstrations, a fallback wallet for the LNURL scheme, an accounts system to mitigate the risk of exposing applications to your full balance.
<br/><br/>
The wallet can run on top of LND, lntxbot, paywall, opennode
<br/><br/>
Please note that although one of the aims of this wallet is to mitigate exposure of all your funds, its still very BETA and may in fact do the opposite!
<br/> <a href="https://github.com/arcbtc/FOSSAW">https://github.com/arcbtc/lnbits</a> </p>
</div><!-- /.box-body -->
</div><!-- /.box -->
</div>
<div class="col-md-4">
<!-- Default box -->
<div class="box">
<div class="box-header">
<h1>
<small>Make a wallet</small>
</h1>
<div class="form-group">
<input type="text" class="form-control" id="walname" placeholder="Name your LNBits wallet" required>
</div>
<button type="button" class="btn btn-primary" onclick="newwallet()">Submit</button>
</div><!-- /.box-body -->
</div><!-- /.box -->
</div>
</div>
</section><!-- /.content -->
</div><!-- /.content-wrapper -->
<footer class="main-footer">
<div class="pull-right hidden-xs">
<b>BETA</b>
</div>
<strong>Learn more about LNbits <a href="https://github.com/arcbtc/FOSSAW">https://github.com/arcbtc/lnbits</a></strong>
</footer>
</div><!-- ./wrapper -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.min.css') }}">
<!-- jQuery 2.1.3 -->
<script src="{{ url_for('static', filename='plugins/jQuery/jQuery-2.1.3.min.js') }}"></script>
<!-- jQuery UI 1.11.2 -->
<script src="http://code.jquery.com/ui/1.11.2/jquery-ui.min.js" type="text/javascript"></script>
<!-- Resolve conflict in jQuery UI tooltip with Bootstrap tooltip -->
<script>
$.widget.bridge('uibutton', $.ui.button);
</script>
<!-- Bootstrap 3.3.2 JS -->
<script src="{{ url_for('static', filename='bootstrap/js/bootstrap.min.js') }}" type="text/javascript"></script>
<!-- Morris.js charts -->
<script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
<script src="{{ url_for('static', filename='plugins/morris/morris.min.js') }}" type="text/javascript"></script>
<!-- Sparkline -->
<script src="{{ url_for('static', filename='plugins/sparkline/jquery.sparkline.min.js') }}" type="text/javascript"></script>
<!-- jvectormap -->
<script src="{{ url_for('static', filename='plugins/jvectormap/jquery-jvectormap-1.2.2.min.js') }}" type="text/javascript"></script>
<script src="{{ url_for('static', filename='plugins/jvectormap/jquery-jvectormap-world-mill-en.js') }}" type="text/javascript"></script>
<!-- jQuery Knob Chart -->
<script src="{{ url_for('static', filename='plugins/knob/jquery.knob.js') }}" type="text/javascript"></script>
<!-- daterangepicker -->
<script src="{{ url_for('static', filename='plugins/daterangepicker/daterangepicker.js') }}" type="text/javascript"></script>
<!-- datepicker -->
<script src="{{ url_for('static', filename='plugins/datepicker/bootstrap-datepicker.js') }}" type="text/javascript"></script>
<!-- Bootstrap WYSIHTML5 -->
<script src="{{ url_for('static', filename='plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.all.min.js') }}" type="text/javascript"></script>
<!-- iCheck -->
<script src="{{ url_for('static', filename='plugins/iCheck/icheck.min.js') }}" type="text/javascript"></script>
<!-- Slimscroll -->
<script src="{{ url_for('static', filename='plugins/slimScroll/jquery.slimscroll.min.js') }}" type="text/javascript"></script>
<!-- FastClick -->
<script src="{{ url_for('static', filename='plugins/fastclick/fastclick.min.js') }}"></script>
<!-- AdminLTE App -->
<script src="{{ url_for('static', filename='dist/js/app.min.js') }}" type="text/javascript"></script>
<!-- AdminLTE dashboard demo (This is only for demo purposes) -->
<script src="{{ url_for('static', filename='dist/js/pages/dashboard.js') }}" type="text/javascript"></script>
<!-- AdminLTE for demo purposes -->
<script src="{{ url_for('static', filename='dist/js/demo.js') }}" type="text/javascript"></script>
<script>
function makeid(length) {
var result = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for ( var i = 0; i < length; i++ ) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
function newwallet() {
walname = document.getElementById("walname").value;
window.location.href = "wallet?usr=" + makeid(40) + "&wal=" + makeid(40) + "&nme=" + walname;
}
</script>
</body>
</html>

View File

@ -0,0 +1,701 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>LNBits Wallet</title>
<meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name='viewport'>
<!-- Bootstrap 3.3.2 -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='bootstrap/css/bootstrap.min.css') }}">
<!-- FontAwesome 4.3.0 -->
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet" type="text/css" />
<!-- Ionicons 2.0.0 -->
<link href="http://code.ionicframework.com/ionicons/2.0.0/css/ionicons.min.css" rel="stylesheet" type="text/css" />
<!-- Theme style -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='dist/css/AdminLTE.min.css') }}">
<!-- AdminLTE Skins. Choose a skin from the css/skins
folder instead of downloading all of them to reduce the load. -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='dist/css/skins/_all-skins.min.css') }}">
<!-- iCheck -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/iCheck/flat/blue.css') }}">
<!-- Morris chart -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/morris/morris.css') }}">
<!-- jvectormap -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/jvectormap/jquery-jvectormap-1.2.2.css') }}">
<!-- Date Picker -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/datepicker/datepicker3.css') }}">
<!-- Daterange picker -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/daterangepicker/daterangepicker-bs3.css') }}">
<!-- bootstrap wysihtml5 - text editor -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.min.css') }}">
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
<![endif]-->
<style>
.small-box>.small-box-footer {
text-align: left;
padding-left: 10px;
}
</style>
<style>
#loadingMessage {
text-align: center;
padding: 40px;
background-color: #eee;
}
#canvas {
width: 100%;
}
#output {
margin-top: 20px;
background: #eee;
padding: 10px;
padding-bottom: 0;
}
#output div {
padding-bottom: 10px;
word-wrap: break-word;
}
#noQRFound {
text-align: center;
}
</style>
</head>
<body class="skin-blue">
<div class="wrapper">
<header class="main-header">
<!-- Logo -->
<a href="/" class="logo"><b>LN</b>bits</a></a>
<!-- Header Navbar: style can be found in header.less -->
<nav class="navbar navbar-static-top" role="navigation">
<!-- Sidebar toggle button-->
<a href="#" class="sidebar-toggle" data-toggle="offcanvas" role="button">
<span class="sr-only">Toggle navigation</span>
</a>
<div class="navbar-custom-menu">
<ul class="nav navbar-nav">
<!-- Messages: style can be found in dropdown.less-->
<li class="dropdown messages-menu">
</li>
</ul>
</div>
</nav>
</header>
<!-- Left side column. contains the logo and sidebar -->
<aside class="main-sidebar">
<!-- sidebar: style can be found in sidebar.less -->
<section class="sidebar">
<!-- Sidebar user panel -->
<!-- /.search form -->
<!-- sidebar menu: : style can be found in sidebar.less -->
<ul class="sidebar-menu">
<li class="header">MAIN NAVIGATIONs</li>
<li class="active treeview">
<a href="#">
<i class="fa fa-bitcoin"></i> <span>Wallets</span>
<i class="fa fa-angle-left pull-right"></i>
</a>
<ul class="treeview-menu">
{% if len > 1 %}
{%for i in range(0, len)%}
<li><a href="wallet?wal={{ thearr[i][0] }}&usr={{ thearr[i][4] }}" ><i class="fa fa-bolt"></i> {{ thearr[i][3] }}</a></li>
{%endfor%}
{% else %}
<li><a href="wallet?wal={{ thewal }}&usr={{ theid }}" ><i class="fa fa-bolt"></i> {{ walnme }}</a></li>
{% endif %}
<li ><a onclick="sidebarmake()">Add a wallet +</a></li>
<div id="sidebarmake"></div>
</ul>
</li>
<li class="treeview">
<a href="#">
<i class="fa fa-th"></i> <span>Extensions</span> <small class="label pull-right bg-green">coming soon</small>
</a>
</li>
</ul>
</section>
<!-- /.sidebar -->
</aside>
<!-- Right side column. Contains the navbar and content of the page -->
<div class="content-wrapper">
<!-- Content Header (Page header) -->
<section class="content-header">
<h1>
Wallet
<small>Control panel <div id="wonga"></div></small>
</h1>
<ol class="breadcrumb">
<li><a href="#"><i class="fa fa-dashboard"></i> Home</a></li>
<li class="active">Wallets</li>
<li href="wallet?wal={{thewal}}" class="active">{{ walnme }}</li>
</ol>
</section>
<!-- Main content -->
<section class="content">
<!-- Small boxes (Stat box) -->
<div class="row">
<div class="col-md-6">
<!-- small box -->
<a href="#" class="small-box-footer">
<div class="small-box bg-aqua">
<div class="inner">
<h3><b>{{ walbal }} sats</b></h3>
<h3>{{ walnme }}</h3>
</div>
<div class="icon">
<i class="ion ion-flash"></i>
</div>
</a>
</div>
</div><!-- ./col -->
</div><!-- /.row -->
<div class="row">
<div class="col-sm-3"> <button onclick="sendfundsinput()" class="btn btn-block btn-primary btn-lg">Send</button></div>
<div class="col-sm-3"> <button onclick="receive()" class="btn btn-block btn-primary btn-lg">Receive</button></div>
</div>
<div id="receive"></div>
<div id="sendfunds"></div>
<div class="row">
<div class="col-md-6">
<div class="box">
<div class="box-header">
<h3 class="box-title">Transactions <b id="demo"></b></h3>
</div><!-- /.box-header -->
<div class="box-body no-padding">
<table id="pagnation" class="table table-bordered table-striped">
<tr>
<th>Memo</th>
<th style='width: 20%'>date</th>
<th style='width: 20%'>amount</th>
</tr>
<tbody id="transactions">
</tbody></table>
</div><!-- /.box-body -->
</div><!-- /.box -->
</div>
</div>
<div id="satschart"></div>
<div class="row">
<div class="col-md-6">
<div class="box box-solid">
<div class="box-header with-border">
</div><!-- /.box-header -->
<div class="box-body">
<div class="box-group" id="accordion">
<!-- we are adding the .panel class so bootstrap.js collapse plugin detects it -->
<div class="panel box box-primary">
<div class="box-header with-border">
<h4 class="box-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapseThree" class="collapsed" aria-expanded="false">
Wallet "{{ walnme }}" API info
</a>
</h4>
</div>
<div id="collapseThree" class="panel-collapse collapse" aria-expanded="false">
<div class="box-body" style='word-wrap: break-word;'>
<b>Admin key: </b><i>{{ adminkey }}</i><br/>
<b>Invoice/Read key: </b><i>{{ inkey }}</i><br/>
Generate an invoice:<br/><code>POST /v1/invoices</code><br/>Header <code>{"Grpc-Metadata-macaroon": "<i>{{ inkey }}</i>"}</code><br/>
Body <code>{"value": "200","memo": "beer"} </code><br/>
Returns <code>{"pay_req": string,"pay_id": string} </code><br/>
*payment will not register in the wallet until the "check invoice" endpoint is used<br/><br/>
Check invoice:<br/>
Check an invoice:<br/><code>GET /v1/invoice/*payment_hash*</code><br/>Header <code>{"Grpc-Metadata-macaroon": "<i>{{ inkey }}</i>"}</code><br/>
Returns <code>{"PAID": "TRUE"}/{"PAID": "FALSE"} </code><br/>
*if using LNTXBOT return will hang until paid<br/><br/>
</div>
</div>
</div>
<div class="panel box box-danger">
<div class="box-header with-border">
<h4 class="box-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapseTwo" class="collapsed" aria-expanded="false">
Delete wallet
</a>
</h4>
</div>
<div id="collapseTwo" class="panel-collapse collapse" aria-expanded="false">
<div class="box-body">
This whole wallet will be deleted, the funds will be UNRECOVERABLE <br/><br/><button class="btn btn-danger" onclick="deletewallet()">Delete wallet</button>
</div>
</div>
</div>
</div>
</div><!-- /.box-body -->
</div><!-- /.box -->
</div>
</div>
</section><!-- /.content -->
</div><!-- /.content-wrapper -->
<footer class="main-footer">
<div class="pull-right hidden-xs">
<b>BETA</b>
</div>
<strong>Learn more about LNbits <a href="https://github.com/arcbtc/FOSSAW">https://github.com/arcbtc/lnbits</a>.</strong>
</footer>
</div><!-- ./wrapper -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.min.css') }}">
<!-- jQuery 2.1.3 -->
<script src="{{ url_for('static', filename='plugins/jQuery/jQuery-2.1.3.min.js') }}"></script>
<!-- jQuery UI 1.11.2 -->
<script src="http://code.jquery.com/ui/1.11.2/jquery-ui.min.js" type="text/javascript"></script>
<!-- Resolve conflict in jQuery UI tooltip with Bootstrap tooltip -->
<script>
$.widget.bridge('uibutton', $.ui.button);
</script>
<!-- Bootstrap 3.3.2 JS -->
<script src="{{ url_for('static', filename='bootstrap/js/bootstrap.min.js') }}" type="text/javascript"></script>
<!-- Morris.js charts -->
<script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
<script src="{{ url_for('static', filename='plugins/morris/morris.min.js') }}" type="text/javascript"></script>
<!-- Sparkline -->
<script src="{{ url_for('static', filename='plugins/sparkline/jquery.sparkline.min.js') }}" type="text/javascript"></script>
<!-- jvectormap -->
<script src="{{ url_for('static', filename='plugins/jvectormap/jquery-jvectormap-1.2.2.min.js') }}" type="text/javascript"></script>
<script src="{{ url_for('static', filename='plugins/jvectormap/jquery-jvectormap-world-mill-en.js') }}" type="text/javascript"></script>
<!-- jQuery Knob Chart -->
<script src="{{ url_for('static', filename='plugins/knob/jquery.knob.js') }}" type="text/javascript"></script>
<!-- daterangepicker -->
<script src="{{ url_for('static', filename='plugins/daterangepicker/daterangepicker.js') }}" type="text/javascript"></script>
<!-- datepicker -->
<script src="{{ url_for('static', filename='plugins/datepicker/bootstrap-datepicker.js') }}" type="text/javascript"></script>
<!-- Bootstrap WYSIHTML5 -->
<script src="{{ url_for('static', filename='plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.all.min.js') }}" type="text/javascript"></script>
<!-- iCheck -->
<script src="{{ url_for('static', filename='plugins/iCheck/icheck.min.js') }}" type="text/javascript"></script>
<!-- Slimscroll -->
<script src="{{ url_for('static', filename='plugins/slimScroll/jquery.slimscroll.min.js') }}" type="text/javascript"></script>
<!-- FastClick -->
<script src="{{ url_for('static', filename='plugins/fastclick/fastclick.min.js') }}"></script>
<!-- AdminLTE App -->
<script src="{{ url_for('static', filename='dist/js/app.min.js') }}" type="text/javascript"></script>
<!-- AdminLTE dashboard demo (This is only for demo purposes) -->
<script src="{{ url_for('static', filename='dist/js/pages/dashboard.js') }}" type="text/javascript"></script>
<!-- AdminLTE for demo purposes -->
<script src="{{ url_for('static', filename='dist/js/demo.js') }}" type="text/javascript"></script>
<script src="{{ url_for('static', filename='plugins/datatables/jquery.dataTables.js') }}" type="text/javascript"></script>
<script src="{{ url_for('static', filename='plugins/datatables/dataTables.bootstrap.js') }}" type="text/javascript"></script>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.min.js"></script>
<script src="{{ url_for('static', filename='plugins/jscam/JS.js') }}" type="text/javascript"></script>
<script src="{{ url_for('static', filename='plugins/jscam/qrcode.min.js') }}" type="text/javascript"></script>
<script src="{{ url_for('static', filename='plugins/bolt11/decoder.js') }}" type="text/javascript"></script>
<script src="{{ url_for('static', filename='plugins/bolt11/utils.js') }}" type="text/javascript"></script>
<script>
window.location.href = "wallet?wal=" + "{{thewal}}" + "&usr=" + "{{theid}}";
var inmacaroon = "{{ inkey }}";
var adminmacaroon = "{{ inkey }}";
var thehash = "";
var theinvoice = "";
var outamount = "";
var outmemo = "";
//API CALLS
function postAjax(url, data, thekey, success) {
var params = typeof data == 'string' ? data : Object.keys(data).map(
function(k){ return encodeURIComponent(k) + '=' + encodeURIComponent(data[k]) }).join('&');
var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
xhr.open('POST', url);
xhr.onreadystatechange = function() {
if (xhr.readyState>3 && xhr.status==200) { success(xhr.responseText); }
};
xhr.setRequestHeader('Grpc-Metadata-macaroon', thekey);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(params);
return xhr;
}
function getAjax(url, thekey,success) {
var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
xhr.open('GET', url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState>3 && xhr.status==200) { success(xhr.responseText); }
};
xhr.setRequestHeader('Grpc-Metadata-macaroon', thekey);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send();
return xhr;
}
function sendfundsinput(){
document.getElementById("sendfunds").innerHTML = "<br/><br/><div class='row'><div class='col-md-4'>" +
"<textarea id='pasteinvoice' class='form-control' rows='3' placeholder='Paste an invoice'></textarea></div></div>" +
"<br/><div class='row'><div class='col-md-4'><button type='submit' onclick='sendfundspaste()' class='btn btn-primary'>" +
"Submit</button><button style='margin-left:20px;' type='submit' class='btn btn-primary' onclick='scanQRsend()'>" +
"Use camera to scan an invoice</button></div></div><br/><br/>";
document.getElementById("receive").innerHTML = "";
}
function sendfundspaste(){
invoice = document.getElementById("pasteinvoice").value;
theinvoice = decode(invoice);
outmemo = theinvoice.data.tags[1].value;
outamount = Number(theinvoice.human_readable_part.amount) /1000;
if (outamount > Number("{{ walbal }}")){
document.getElementById("sendfunds").innerHTML = "<div class='row'><div class='col-md-6'>"+
"<h3><b style='color:red;'>Not enough funds!</b></h3>" +
"<button style='margin-left:20px;' type='submit' class='btn btn-primary' onclick='cancelsend()'>Continue</button>"+
"</br/></br/></div></div>";
}
else{
document.getElementById("sendfunds").innerHTML = "<div class='row'><div class='col-md-6'>"+
"<h3><b>Invoice details</b></br/>Amount: " + outamount + "<br/>Memo: " + outmemo + "</h3>" +
"<h4 style='word-wrap: break-word;'>" + invoice + "</h4>"+
"<button type='submit' class='btn btn-primary' onclick='sendfunds(" + JSON.stringify(invoice) + ")'>Send funds</button>"+
"<button style='margin-left:20px;' type='submit' class='btn btn-primary' onclick='cancelsend()'>Cancel payment</button>"+
"</br/></br/></div></div>";
}
}
function receive() {
document.getElementById("receive").innerHTML = "<br/><div class='row'><div id='QRCODE'>" +
"<div class='col-sm-2'><input type='number' class='form-control' id='amount' placeholder='Amount' max='1000000' required></div>" +
"<div class='col-sm-2'><input type='text' class='form-control' id='memo' placeholder='Memo' required></div>" +
"<div class='col-sm-2'><input type='button' id='getinvoice' onclick='received()' class='btn btn-primary' value='Send Ajax request' /></div>" +
"</div></div><br/>";
document.getElementById("sendfunds").innerHTML = "";
}
function received(){
memo = document.getElementById("memo").value;
amount = document.getElementById("amount").value;
postAjax('/v1/invoices', JSON.stringify({'value' : amount, 'memo' : memo}), '{{ inkey }}', function(data){
theinvoice = JSON.parse(data).pay_req;
thehash = JSON.parse(data).payment_hash;
document.getElementById("QRCODE").innerHTML = "<div class='col-md-4'><div class='box'><div class='box-header'>" +
"<center><a href='lightning:" + theinvoice + "'><div id='qrcode'></div></a>" +
"<p style='word-wrap: break-word;'>" + theinvoice + "</p></div></div></div></center>";
new QRCode(document.getElementById("qrcode"), {
text: theinvoice,
width: 300,
height: 300,
colorDark : "#000000",
colorLight : "#ffffff",
correctLevel : QRCode.CorrectLevel.M
});
getAjax('/v1/invoice/'+ thehash, '{{ inkey }}', function(datab){
console.log(JSON.parse(datab).PAID);
if (JSON.parse(datab).PAID == 'TRUE'){
window.location.href = "wallet?wal=" + "{{thewal}}" + "&usr=" + "{{theid}}";
}
});
});
}
function cancelsend(){
window.location.href = "wallet?wal=" + "{{thewal}}" + "&usr=" + "{{theid}}";
}
function sendfunds(invoice){
var url = '/v1/channels/transactions';
postAjax(url, JSON.stringify({'payment_request' : invoice}), '{{ adminkey }}', function(data){
thehash = JSON.parse(data).payment_hash;
console.log(JSON.parse(data));
if (JSON.parse(data).PAID == 'TRUE'){
window.location.href = "wallet?wal=" + "{{thewal}}" + "&usr=" + "{{theid}}";
}
});
}
function scanQRsend(){
document.getElementById("sendfunds").innerHTML = "<br/><br/><div class='row'><div class='col-md-4'>" +
"<div id='loadingMessage'>🎥 Unable to access video stream (please make sure you have a webcam enabled)</div>" +
"<canvas id='canvas' hidden></canvas><div id='output' hidden><div id='outputMessage'></div>" +
"<br/><span id='outputData'></span></div></div></div><button type='submit' class='btn btn-primary' onclick='cancelsend()'>Cancel</button><br/><br/>";
var video = document.createElement("video");
var canvasElement = document.getElementById("canvas");
var canvas = canvasElement.getContext("2d");
var loadingMessage = document.getElementById("loadingMessage");
var outputContainer = document.getElementById("output");
var outputMessage = document.getElementById("outputMessage");
var outputData = document.getElementById("outputData");
function drawLine(begin, end, color) {
canvas.beginPath();
canvas.moveTo(begin.x, begin.y);
canvas.lineTo(end.x, end.y);
canvas.lineWidth = 4;
canvas.strokeStyle = color;
canvas.stroke();
}
// Use facingMode: environment to attemt to get the front camera on phones
navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } }).then(function(stream) {
video.srcObject = stream;
video.setAttribute("playsinline", true); // required to tell iOS safari we don't want fullscreen
video.play();
requestAnimationFrame(tick);
});
function tick() {
loadingMessage.innerText = "⌛ Loading video..."
if (video.readyState === video.HAVE_ENOUGH_DATA) {
loadingMessage.hidden = true;
canvasElement.hidden = false;
outputContainer.hidden = false;
canvasElement.height = video.videoHeight;
canvasElement.width = video.videoWidth;
canvas.drawImage(video, 0, 0, canvasElement.width, canvasElement.height);
var imageData = canvas.getImageData(0, 0, canvasElement.width, canvasElement.height);
var code = jsQR(imageData.data, imageData.width, imageData.height, {
inversionAttempts: "dontInvert",
});
if (code) {
drawLine(code.location.topLeftCorner, code.location.topRightCorner, "#FF3B58");
drawLine(code.location.topRightCorner, code.location.bottomRightCorner, "#FF3B58");
drawLine(code.location.bottomRightCorner, code.location.bottomLeftCorner, "#FF3B58");
drawLine(code.location.bottomLeftCorner, code.location.topLeftCorner, "#FF3B58");
outputMessage.hidden = true;
outputData.parentElement.hidden = false;
outputData.innerText = JSON.stringify(code.data);
theinvoice = decode(code.data);
outmemo = theinvoice.data.tags[1].value;
outamount = Number(theinvoice.human_readable_part.amount) /1000;
if (outamount > Number("{{ walbal }}")){
document.getElementById("sendfunds").innerHTML = "<div class='row'><div class='col-md-6'>"+
"<h3><b style='color:red;'>Not enough funds!</b></h3>" +
"<button style='margin-left:20px;' type='submit' class='btn btn-primary' onclick='cancelsend()'>Continue</button>"+
"</br/></br/></div></div>";
}
else{
document.getElementById("sendfunds").innerHTML = "<div class='row'><div class='col-md-6'>"+
"<h3><b>Invoice details</b></br/>Amount: " + outamount + "<br/>Memo: " + outmemo + "</h3>" +
"<h4 style='word-wrap: break-word;'>" + JSON.stringify(code.data) + "</h4>"+
"<button type='submit' class='btn btn-primary' onclick='sendfunds(" + JSON.stringify(code.data) + ")'>Send funds</button>"+
"<button style='margin-left:20px;' type='submit' class='btn btn-primary' onclick='cancelsend()'>Cancel payment</button>"+
"</br/></br/></div></div>";
}
}
else {
outputMessage.hidden = false;
outputData.parentElement.hidden = true;
}
}
requestAnimationFrame(tick);
}
}
function deletewallet(){
var urll = "deletewallet?wal={{ thewal }}&usr={{ theid }}";
window.location.href = urll;
}
function sidebarmake(){
document.getElementById("sidebarmake").innerHTML = "<li><div class='form-group'>"+
"<input style='width:70%;float:left;' type='text' class='form-control' id='walname' placeholder='Name wallet' required>" +
"<button style='width:30%;float:left;' type='button' class='btn btn-primary' onclick='newwallet()'>Submit</button>" +
"</div></li><br/><br/>";
}
function makeid(length) {
var result = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for ( var i = 0; i < length; i++ ) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
function newwallet() {
walname = document.getElementById("walname").value;
window.location.href = "wallet?usr=" + "{{ theid }}" + "&wal=" + makeid(40) + "&nme=" + walname;
}
var trans = "{{ transactions }}";
var transac = trans.substr(1);
//console.log(trans);
if(transac != ""){
var transactions = "";
var linechart = [];
var tran = transac.split("!");
tran.shift();
console.log(tran);
for (var i = 0; i < tran.length; i++) {
rects = tran[i].split(",");
rectstime = String(rects[1]).split(".");
var datime = convertTimestamp(rectstime[0])
//Make the transactions table
transactions += "<tr><td style='width: 50%'>" + rects[0] + "</td><td>" + datime + "</td><td>" + rects[2] + "</td></tr>";
//Make the line chart
if (i == 0){
linechart.push({y: datime, item1: rects[2]});
}
else{
linechart.push({y: datime, item1: rects[3]});
}
}
document.getElementById("transactions").innerHTML = transactions;
if(linechart[0] != ""){
document.getElementById("satschart").innerHTML = "<div class='row'><div class='col-md-6'><div class='box box-info'><div class='box-header'>" +
"<h3 class='box-title'>Spending</h3></div><div class='box-body chart-responsive'>" +
"<div class='chart' id='line-chart' style='height: 300px;'></div></div></div></div></div>";
}
}
console.log(linechart);
var data = linechart
var line = new Morris.Line({
element: 'line-chart',
resize: true,
data,
xkey: 'y',
ykeys: ['item1'],
labels: ['Item 1'],
lineColors: ['#3c8dbc'],
hideHover: 'auto'
});
function convertTimestamp(timestamp) {
var d = new Date(timestamp * 1000),
yyyy = d.getFullYear(),
mm = ('0' + (d.getMonth() + 1)).slice(-2),
dd = ('0' + d.getDate()).slice(-2),
hh = d.getHours(),
h = hh,
min = ('0' + d.getMinutes()).slice(-2),
ampm = 'AM',
time;
time = yyyy + '-' + mm + '-' + dd + ' ' + h + ':' + min;
return time;
}
</script>
</body>
</html>

View File

@ -0,0 +1,717 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>LNBits Wallet</title>
<meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name='viewport'>
<!-- Bootstrap 3.3.2 -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='bootstrap/css/bootstrap.min.css') }}">
<!-- FontAwesome 4.3.0 -->
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet" type="text/css" />
<!-- Ionicons 2.0.0 -->
<link href="http://code.ionicframework.com/ionicons/2.0.0/css/ionicons.min.css" rel="stylesheet" type="text/css" />
<!-- Theme style -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='dist/css/AdminLTE.min.css') }}">
<!-- AdminLTE Skins. Choose a skin from the css/skins
folder instead of downloading all of them to reduce the load. -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='dist/css/skins/_all-skins.min.css') }}">
<!-- iCheck -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/iCheck/flat/blue.css') }}">
<!-- Morris chart -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/morris/morris.css') }}">
<!-- jvectormap -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/jvectormap/jquery-jvectormap-1.2.2.css') }}">
<!-- Date Picker -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/datepicker/datepicker3.css') }}">
<!-- Daterange picker -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/daterangepicker/daterangepicker-bs3.css') }}">
<!-- bootstrap wysihtml5 - text editor -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.min.css') }}">
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
<![endif]-->
<style>
.small-box>.small-box-footer {
text-align: left;
padding-left: 10px;
}
</style>
<style>
#loadingMessage {
text-align: center;
padding: 40px;
background-color: #eee;
}
#canvas {
width: 100%;
}
#output {
margin-top: 20px;
background: #eee;
padding: 10px;
padding-bottom: 0;
}
#output div {
padding-bottom: 10px;
word-wrap: break-word;
}
#noQRFound {
text-align: center;
}
</style>
</head>
<body class="skin-blue">
<div class="wrapper">
<header class="main-header">
<!-- Logo -->
<a href="/" class="logo"><b>LN</b>bits</a></a>
<!-- Header Navbar: style can be found in header.less -->
<nav class="navbar navbar-static-top" role="navigation">
<!-- Sidebar toggle button-->
<a href="#" class="sidebar-toggle" data-toggle="offcanvas" role="button">
<span class="sr-only">Toggle navigation</span>
</a>
<div class="navbar-custom-menu">
<ul class="nav navbar-nav">
<!-- Messages: style can be found in dropdown.less-->
<!-- Notifications: style can be found in dropdown.less -->
<li class="dropdown notifications-menu">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-bell-o"></i>
<span class="label label-danger">!</span>
</a>
<ul class="dropdown-menu">
<li class="header"><b>Instant wallet, bookmark to save</b></li>
<li>
</li>
</ul>
</li>
</ul>
</div>
<div class="navbar-custom-menu">
<ul class="nav navbar-nav">
<!-- Messages: style can be found in dropdown.less-->
<li class="dropdown messages-menu">
</li>
</ul>
</div>
</nav>
</header>
<!-- Left side column. contains the logo and sidebar -->
<aside class="main-sidebar">
<!-- sidebar: style can be found in sidebar.less -->
<section class="sidebar">
<!-- Sidebar user panel -->
<!-- /.search form -->
<!-- sidebar menu: : style can be found in sidebar.less -->
<ul class="sidebar-menu">
<li class="header">MAIN NAVIGATIONs</li>
<li class="active treeview">
<a href="#">
<i class="fa fa-bitcoin"></i> <span>Wallets</span>
<i class="fa fa-angle-left pull-right"></i>
</a>
<ul class="treeview-menu">
{% if len > 1 %}
{%for i in range(0, len)%}
<li><a href="wallet?wal={{ thearr[i][0] }}&usr={{ thearr[i][4] }}" ><i class="fa fa-bolt"></i> {{ thearr[i][3] }}</a></li>
{%endfor%}
{% else %}
<li><a href="wallet?wal={{ thewal }}&usr={{ theid }}" ><i class="fa fa-bolt"></i> {{ walnme }}</a></li>
{% endif %}
<li ><a onclick="sidebarmake()">Add a wallet +</a></li>
<div id="sidebarmake"></div>
</ul>
</li>
<li class="treeview">
<a href="#">
<i class="fa fa-th"></i> <span>Extensions</span> <small class="label pull-right bg-green">coming soon</small>
</a>
</li>
</ul>
</section>
<!-- /.sidebar -->
</aside>
<!-- Right side column. Contains the navbar and content of the page -->
<div class="content-wrapper">
<!-- Content Header (Page header) -->
<section class="content-header">
<h1>
Wallet
<small>Control panel <div id="wonga"></div></small>
</h1>
<ol class="breadcrumb">
<li><a href="#"><i class="fa fa-dashboard"></i> Home</a></li>
<li class="active">Wallets</li>
<li href="wallet?wal={{thewal}}" class="active">{{ walnme }}</li>
</ol>
</section>
<!-- Main content -->
<section class="content">
<!-- Small boxes (Stat box) -->
<div class="row">
<div class="col-md-6">
<!-- small box -->
<a href="#" class="small-box-footer">
<div class="small-box bg-aqua">
<div class="inner">
<h3><b>{{ walbal }} sats</b></h3>
<h3>{{ walnme }}</h3>
</div>
<div class="icon">
<i class="ion ion-flash"></i>
</div>
</a>
</div>
</div><!-- ./col -->
</div><!-- /.row -->
<div class="row">
<div class="col-sm-3"> <button onclick="sendfundsinput()" class="btn btn-block btn-primary btn-lg">Send</button></div>
<div class="col-sm-3"> <button onclick="receive()" class="btn btn-block btn-primary btn-lg">Receive</button></div>
</div>
<div id="receive"></div>
<div id="sendfunds"></div>
<div class="row">
<div class="col-md-6">
<div class="box">
<div class="box-header">
<h3 class="box-title">Transactions <b id="demo"></b></h3>
</div><!-- /.box-header -->
<div class="box-body no-padding">
<table id="pagnation" class="table table-bordered table-striped">
<tr>
<th>Memo</th>
<th style='width: 20%'>date</th>
<th style='width: 20%'>amount</th>
</tr>
<tbody id="transactions">
</tbody></table>
</div><!-- /.box-body -->
</div><!-- /.box -->
</div>
</div>
<div id="satschart"></div>
<div class="row">
<div class="col-md-6">
<div class="box box-solid">
<div class="box-header with-border">
</div><!-- /.box-header -->
<div class="box-body">
<div class="box-group" id="accordion">
<!-- we are adding the .panel class so bootstrap.js collapse plugin detects it -->
<div class="panel box box-primary">
<div class="box-header with-border">
<h4 class="box-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapseThree" class="collapsed" aria-expanded="false">
Wallet "{{ walnme }}" API info
</a>
</h4>
</div>
<div id="collapseThree" class="panel-collapse collapse" aria-expanded="false">
<div class="box-body" style='word-wrap: break-word;'>
<b>Admin key: </b><i>{{ adminkey }}</i><br/>
<b>Invoice/Read key: </b><i>{{ inkey }}</i><br/>
Generate an invoice:<br/><code>POST /v1/invoices</code><br/>Header <code>{"Grpc-Metadata-macaroon": "<i>{{ inkey }}</i>"}</code><br/>
Body <code>{"value": "200","memo": "beer"} </code><br/>
Returns <code>{"pay_req": string,"pay_id": string} </code><br/>
*payment will not register in the wallet until the "check invoice" endpoint is used<br/><br/>
Check invoice:<br/>
Check an invoice:<br/><code>GET /v1/invoice/*payment_hash*</code><br/>Header <code>{"Grpc-Metadata-macaroon": "<i>{{ inkey }}</i>"}</code><br/>
Returns <code>{"PAID": "TRUE"}/{"PAID": "FALSE"} </code><br/>
*if using LNTXBOT return will hang until paid<br/><br/>
</div>
</div>
</div>
<div class="panel box box-danger">
<div class="box-header with-border">
<h4 class="box-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapseTwo" class="collapsed" aria-expanded="false">
Delete wallet
</a>
</h4>
</div>
<div id="collapseTwo" class="panel-collapse collapse" aria-expanded="false">
<div class="box-body">
This whole wallet will be deleted, the funds will be UNRECOVERABLE <br/><br/><button class="btn btn-danger" onclick="deletewallet()">Delete wallet</button>
</div>
</div>
</div>
</div>
</div><!-- /.box-body -->
</div><!-- /.box -->
</div>
</div>
</section><!-- /.content -->
</div><!-- /.content-wrapper -->
<footer class="main-footer">
<div class="pull-right hidden-xs">
<b>BETA</b>
</div>
<strong><i style="color:red;">WARNING - LNbits is still in BETA! </i>Learn more about LNbits <a href="https://github.com/arcbtc/FOSSAW">https://github.com/arcbtc/lnbits</a>. </strong>
</footer>
</div><!-- ./wrapper -->
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.min.css') }}">
<!-- jQuery 2.1.3 -->
<script src="{{ url_for('static', filename='plugins/jQuery/jQuery-2.1.3.min.js') }}"></script>
<!-- jQuery UI 1.11.2 -->
<script src="http://code.jquery.com/ui/1.11.2/jquery-ui.min.js" type="text/javascript"></script>
<!-- Resolve conflict in jQuery UI tooltip with Bootstrap tooltip -->
<script>
$.widget.bridge('uibutton', $.ui.button);
</script>
<!-- Bootstrap 3.3.2 JS -->
<script src="{{ url_for('static', filename='bootstrap/js/bootstrap.min.js') }}" type="text/javascript"></script>
<!-- Morris.js charts -->
<script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
<script src="{{ url_for('static', filename='plugins/morris/morris.min.js') }}" type="text/javascript"></script>
<!-- Sparkline -->
<script src="{{ url_for('static', filename='plugins/sparkline/jquery.sparkline.min.js') }}" type="text/javascript"></script>
<!-- jvectormap -->
<script src="{{ url_for('static', filename='plugins/jvectormap/jquery-jvectormap-1.2.2.min.js') }}" type="text/javascript"></script>
<script src="{{ url_for('static', filename='plugins/jvectormap/jquery-jvectormap-world-mill-en.js') }}" type="text/javascript"></script>
<!-- jQuery Knob Chart -->
<script src="{{ url_for('static', filename='plugins/knob/jquery.knob.js') }}" type="text/javascript"></script>
<!-- daterangepicker -->
<script src="{{ url_for('static', filename='plugins/daterangepicker/daterangepicker.js') }}" type="text/javascript"></script>
<!-- datepicker -->
<script src="{{ url_for('static', filename='plugins/datepicker/bootstrap-datepicker.js') }}" type="text/javascript"></script>
<!-- Bootstrap WYSIHTML5 -->
<script src="{{ url_for('static', filename='plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.all.min.js') }}" type="text/javascript"></script>
<!-- iCheck -->
<script src="{{ url_for('static', filename='plugins/iCheck/icheck.min.js') }}" type="text/javascript"></script>
<!-- Slimscroll -->
<script src="{{ url_for('static', filename='plugins/slimScroll/jquery.slimscroll.min.js') }}" type="text/javascript"></script>
<!-- FastClick -->
<script src="{{ url_for('static', filename='plugins/fastclick/fastclick.min.js') }}"></script>
<!-- AdminLTE App -->
<script src="{{ url_for('static', filename='dist/js/app.min.js') }}" type="text/javascript"></script>
<!-- AdminLTE dashboard demo (This is only for demo purposes) -->
<script src="{{ url_for('static', filename='dist/js/pages/dashboard.js') }}" type="text/javascript"></script>
<!-- AdminLTE for demo purposes -->
<script src="{{ url_for('static', filename='dist/js/demo.js') }}" type="text/javascript"></script>
<script src="{{ url_for('static', filename='plugins/datatables/jquery.dataTables.js') }}" type="text/javascript"></script>
<script src="{{ url_for('static', filename='plugins/datatables/dataTables.bootstrap.js') }}" type="text/javascript"></script>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.min.js"></script>
<script src="{{ url_for('static', filename='plugins/jscam/JS.js') }}" type="text/javascript"></script>
<script src="{{ url_for('static', filename='plugins/jscam/qrcode.min.js') }}" type="text/javascript"></script>
<script src="{{ url_for('static', filename='plugins/bolt11/decoder.js') }}" type="text/javascript"></script>
<script src="{{ url_for('static', filename='plugins/bolt11/utils.js') }}" type="text/javascript"></script>
<script>
var inmacaroon = "{{ inkey }}";
var adminmacaroon = "{{ inkey }}";
var thehash = "";
var theinvoice = "";
var outamount = "";
var outmemo = "";
//API CALLS
function postAjax(url, data, thekey, success) {
var params = typeof data == 'string' ? data : Object.keys(data).map(
function(k){ return encodeURIComponent(k) + '=' + encodeURIComponent(data[k]) }).join('&');
var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
xhr.open('POST', url);
xhr.onreadystatechange = function() {
if (xhr.readyState>3 && xhr.status==200) { success(xhr.responseText); }
};
xhr.setRequestHeader('Grpc-Metadata-macaroon', thekey);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(params);
return xhr;
}
function getAjax(url, thekey,success) {
var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
xhr.open('GET', url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState>3 && xhr.status==200) { success(xhr.responseText); }
};
xhr.setRequestHeader('Grpc-Metadata-macaroon', thekey);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send();
return xhr;
}
function sendfundsinput(){
document.getElementById("sendfunds").innerHTML = "<br/><br/><div class='row'><div class='col-md-4'>" +
"<textarea id='pasteinvoice' class='form-control' rows='3' placeholder='Paste an invoice'></textarea></div></div>" +
"<br/><div class='row'><div class='col-md-4'><button type='submit' onclick='sendfundspaste()' class='btn btn-primary'>" +
"Submit</button><button style='margin-left:20px;' type='submit' class='btn btn-primary' onclick='scanQRsend()'>" +
"Use camera to scan an invoice</button></div></div><br/><br/>";
document.getElementById("receive").innerHTML = "";
}
function sendfundspaste(){
invoice = document.getElementById("pasteinvoice").value;
theinvoice = decode(invoice);
outmemo = theinvoice.data.tags[1].value;
outamount = Number(theinvoice.human_readable_part.amount) /1000;
if (outamount > Number("{{ walbal }}")){
document.getElementById("sendfunds").innerHTML = "<div class='row'><div class='col-md-6'>"+
"<h3><b style='color:red;'>Not enough funds!</b></h3>" +
"<button style='margin-left:20px;' type='submit' class='btn btn-primary' onclick='cancelsend()'>Continue</button>"+
"</br/></br/></div></div>";
}
else{
document.getElementById("sendfunds").innerHTML = "<div class='row'><div class='col-md-6'>"+
"<h3><b>Invoice details</b></br/>Amount: " + outamount + "<br/>Memo: " + outmemo + "</h3>" +
"<h4 style='word-wrap: break-word;'>" + invoice + "</h4>"+
"<button type='submit' class='btn btn-primary' onclick='sendfunds(" + JSON.stringify(invoice) + ")'>Send funds</button>"+
"<button style='margin-left:20px;' type='submit' class='btn btn-primary' onclick='cancelsend()'>Cancel payment</button>"+
"</br/></br/></div></div>";
}
}
function receive() {
document.getElementById("receive").innerHTML = "<br/><div class='row'><div id='QRCODE'>" +
"<div class='col-sm-2'><input type='number' class='form-control' id='amount' placeholder='Amount' max='1000000' required></div>" +
"<div class='col-sm-2'><input type='text' class='form-control' id='memo' placeholder='Memo' required></div>" +
"<div class='col-sm-2'><input type='button' id='getinvoice' onclick='received()' class='btn btn-primary' value='Send Ajax request' /></div>" +
"</div></div><br/>";
document.getElementById("sendfunds").innerHTML = "";
}
function received(){
memo = document.getElementById("memo").value;
amount = document.getElementById("amount").value;
postAjax('/v1/invoices', JSON.stringify({'value' : amount, 'memo' : memo}), '{{ inkey }}', function(data){
theinvoice = JSON.parse(data).pay_req;
thehash = JSON.parse(data).payment_hash;
document.getElementById("QRCODE").innerHTML = "<div class='col-md-4'><div class='box'><div class='box-header'>" +
"<center><a href='lightning:" + theinvoice + "'><div id='qrcode'></div></a>" +
"<p style='word-wrap: break-word;'>" + theinvoice + "</p></div></div></div></center>";
new QRCode(document.getElementById("qrcode"), {
text: theinvoice,
width: 300,
height: 300,
colorDark : "#000000",
colorLight : "#ffffff",
correctLevel : QRCode.CorrectLevel.M
});
getAjax('/v1/invoice/'+ thehash, '{{ inkey }}', function(datab){
console.log(JSON.parse(datab).PAID);
if (JSON.parse(datab).PAID == 'TRUE'){
window.location.href = "wallet?wal=" + "{{thewal}}" + "&usr=" + "{{theid}}";
}
});
});
}
function cancelsend(){
window.location.href = "wallet?wal=" + "{{thewal}}" + "&usr=" + "{{theid}}";
}
function sendfunds(invoice){
var url = '/v1/channels/transactions';
postAjax(url, JSON.stringify({'payment_request' : invoice}), '{{ adminkey }}', function(data){
thehash = JSON.parse(data).payment_hash;
console.log(JSON.parse(data));
if (JSON.parse(data).PAID == 'TRUE'){
window.location.href = "wallet?wal=" + "{{thewal}}" + "&usr=" + "{{theid}}";
}
});
}
function scanQRsend(){
document.getElementById("sendfunds").innerHTML = "<br/><br/><div class='row'><div class='col-md-4'>" +
"<div id='loadingMessage'>🎥 Unable to access video stream (please make sure you have a webcam enabled)</div>" +
"<canvas id='canvas' hidden></canvas><div id='output' hidden><div id='outputMessage'></div>" +
"<br/><span id='outputData'></span></div></div></div><button type='submit' class='btn btn-primary' onclick='cancelsend()'>Cancel</button><br/><br/>";
var video = document.createElement("video");
var canvasElement = document.getElementById("canvas");
var canvas = canvasElement.getContext("2d");
var loadingMessage = document.getElementById("loadingMessage");
var outputContainer = document.getElementById("output");
var outputMessage = document.getElementById("outputMessage");
var outputData = document.getElementById("outputData");
function drawLine(begin, end, color) {
canvas.beginPath();
canvas.moveTo(begin.x, begin.y);
canvas.lineTo(end.x, end.y);
canvas.lineWidth = 4;
canvas.strokeStyle = color;
canvas.stroke();
}
// Use facingMode: environment to attemt to get the front camera on phones
navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } }).then(function(stream) {
video.srcObject = stream;
video.setAttribute("playsinline", true); // required to tell iOS safari we don't want fullscreen
video.play();
requestAnimationFrame(tick);
});
function tick() {
loadingMessage.innerText = "⌛ Loading video..."
if (video.readyState === video.HAVE_ENOUGH_DATA) {
loadingMessage.hidden = true;
canvasElement.hidden = false;
outputContainer.hidden = false;
canvasElement.height = video.videoHeight;
canvasElement.width = video.videoWidth;
canvas.drawImage(video, 0, 0, canvasElement.width, canvasElement.height);
var imageData = canvas.getImageData(0, 0, canvasElement.width, canvasElement.height);
var code = jsQR(imageData.data, imageData.width, imageData.height, {
inversionAttempts: "dontInvert",
});
if (code) {
drawLine(code.location.topLeftCorner, code.location.topRightCorner, "#FF3B58");
drawLine(code.location.topRightCorner, code.location.bottomRightCorner, "#FF3B58");
drawLine(code.location.bottomRightCorner, code.location.bottomLeftCorner, "#FF3B58");
drawLine(code.location.bottomLeftCorner, code.location.topLeftCorner, "#FF3B58");
outputMessage.hidden = true;
outputData.parentElement.hidden = false;
outputData.innerText = JSON.stringify(code.data);
theinvoice = decode(code.data);
outmemo = theinvoice.data.tags[1].value;
outamount = Number(theinvoice.human_readable_part.amount) /1000;
if (outamount > Number("{{ walbal }}")){
document.getElementById("sendfunds").innerHTML = "<div class='row'><div class='col-md-6'>"+
"<h3><b style='color:red;'>Not enough funds!</b></h3>" +
"<button style='margin-left:20px;' type='submit' class='btn btn-primary' onclick='cancelsend()'>Continue</button>"+
"</br/></br/></div></div>";
}
else{
document.getElementById("sendfunds").innerHTML = "<div class='row'><div class='col-md-6'>"+
"<h3><b>Invoice details</b></br/>Amount: " + outamount + "<br/>Memo: " + outmemo + "</h3>" +
"<h4 style='word-wrap: break-word;'>" + JSON.stringify(code.data) + "</h4>"+
"<button type='submit' class='btn btn-primary' onclick='sendfunds(" + JSON.stringify(code.data) + ")'>Send funds</button>"+
"<button style='margin-left:20px;' type='submit' class='btn btn-primary' onclick='cancelsend()'>Cancel payment</button>"+
"</br/></br/></div></div>";
}
}
else {
outputMessage.hidden = false;
outputData.parentElement.hidden = true;
}
}
requestAnimationFrame(tick);
}
}
function deletewallet(){
var urll = "deletewallet?wal={{ thewal }}&usr={{ theid }}";
window.location.href = urll;
}
function sidebarmake(){
document.getElementById("sidebarmake").innerHTML = "<li><div class='form-group'>"+
"<input style='width:70%;float:left;' type='text' class='form-control' id='walname' placeholder='Name wallet' required>" +
"<button style='width:30%;float:left;' type='button' class='btn btn-primary' onclick='newwallet()'>Submit</button>" +
"</div></li><br/><br/>";
}
function makeid(length) {
var result = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for ( var i = 0; i < length; i++ ) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
function newwallet() {
walname = document.getElementById("walname").value;
window.location.href = "wallet?usr=" + "{{ theid }}" + "&wal=" + makeid(40) + "&nme=" + walname;
}
var trans = "{{ transactions }}";
var transac = trans.substr(1);
//console.log(trans);
if(transac != ""){
var transactions = "";
var linechart = [];
var tran = transac.split("!");
tran.shift();
console.log(tran);
for (var i = 0; i < tran.length; i++) {
rects = tran[i].split(",");
rectstime = String(rects[1]).split(".");
var datime = convertTimestamp(rectstime[0])
//Make the transactions table
transactions += "<tr><td style='width: 50%'>" + rects[0] + "</td><td>" + datime + "</td><td>" + rects[2] + "</td></tr>";
//Make the line chart
if (i == 0){
linechart.push({y: datime, item1: rects[2]});
}
else{
linechart.push({y: datime, item1: rects[3]});
}
}
document.getElementById("transactions").innerHTML = transactions;
if(linechart[0] != ""){
document.getElementById("satschart").innerHTML = "<div class='row'><div class='col-md-6'><div class='box box-info'><div class='box-header'>" +
"<h3 class='box-title'>Spending</h3></div><div class='box-body chart-responsive'>" +
"<div class='chart' id='line-chart' style='height: 300px;'></div></div></div></div></div>";
}
}
console.log(linechart);
var data = linechart
var line = new Morris.Line({
element: 'line-chart',
resize: true,
data,
xkey: 'y',
ykeys: ['item1'],
labels: ['Item 1'],
lineColors: ['#3c8dbc'],
hideHover: 'auto'
});
function convertTimestamp(timestamp) {
var d = new Date(timestamp * 1000),
yyyy = d.getFullYear(),
mm = ('0' + (d.getMonth() + 1)).slice(-2),
dd = ('0' + d.getDate()).slice(-2),
hh = d.getHours(),
h = hh,
min = ('0' + d.getMinutes()).slice(-2),
ampm = 'AM',
time;
time = yyyy + '-' + mm + '-' + dd + ' ' + h + ':' + min;
return time;
}
</script>
</body>
</html>