Browse Source

big bundle of changes.

* reorganize templates even more (and a small layout break)
* rename wallets.hash and accounts.userhash to id
* refactor /wallets so it's idempotent for each param combination
* many small changes
fee_issues
fiatjaf 5 years ago
parent
commit
14dfa9ecc6
  1. 235
      LNbits/__init__.py
  2. 8
      LNbits/data/schema.sql
  3. 1
      LNbits/db.py
  4. 5
      LNbits/helpers.py
  5. 1
      LNbits/settings.py
  6. 55
      LNbits/templates/base.html
  7. 80
      LNbits/templates/index.html
  8. 716
      LNbits/templates/wallet.html

235
LNbits/__init__.py

@ -1,13 +1,13 @@
import lnurl import lnurl
import uuid
import os import os
import requests import requests
from flask import Flask, jsonify, render_template, request from flask import Flask, jsonify, render_template, request, redirect, url_for
from . import bolt11 from . import bolt11
from .db import Database from .db import Database
from .helpers import encrypt from .settings import DATABASE_PATH, LNBITS_PATH, WALLET, DEFAULT_USER_WALLET_NAME
from .settings import DATABASE_PATH, LNBITS_PATH, WALLET
app = Flask(__name__) app = Flask(__name__)
@ -35,21 +35,24 @@ def home():
@app.route("/deletewallet") @app.route("/deletewallet")
def deletewallet(): def deletewallet():
theid = request.args.get("usr")
thewal = request.args.get("wal") thewal = request.args.get("wal")
with Database() as db: with Database() as db:
wallet_row = db.fetchone("SELECT * FROM wallets WHERE hash = ?", (thewal,)) db.execute(
"""
if not wallet_row: UPDATE wallets AS w SET
return render_template("index.html") user = 'del:' || w.user,
adminkey = 'del:' || w.adminkey,
db.execute("UPDATE wallets SET user = ? WHERE hash = ?", (f"del{wallet_row[4]}", wallet_row[0])) inkey = 'del:' || w.inkey
db.execute("UPDATE wallets SET adminkey = ? WHERE hash = ?", (f"del{wallet_row[5]}", wallet_row[0])) WHERE id = ? AND user = ?
db.execute("UPDATE wallets SET inkey = ? WHERE hash = ?", (f"del{wallet_row[6]}", wallet_row[0])) """,
(thewal, theid),
)
user_wallets = db.fetchall("SELECT * FROM wallets WHERE user = ?", (wallet_row[4],)) next_wallet = db.fetchone("SELECT hash FROM wallets WHERE user = ?", (theid,))
if user_wallets: if next_wallet:
return render_template("deletewallet.html", theid=user_wallets[0][4], thewal=user_wallets[0][0]) return redirect(url_for("wallet", usr=theid, wal=next_wallet[0]))
return render_template("index.html") return render_template("index.html")
@ -60,13 +63,13 @@ def lnurlwallet():
invoice = WALLET.create_invoice(withdraw_res.max_sats).json() invoice = WALLET.create_invoice(withdraw_res.max_sats).json()
payment_hash = invoice["payment_hash"] payment_hash = invoice["payment_hash"]
rrr = requests.get( r = requests.get(
withdraw_res.callback.base, withdraw_res.callback.base,
params={**withdraw_res.callback.query_params, **{"k1": withdraw_res.k1, "pr": invoice["pay_req"]}}, params={**withdraw_res.callback.query_params, **{"k1": withdraw_res.k1, "pr": invoice["pay_req"]}},
) )
dataaa = rrr.json() data = r.json()
if dataaa["status"] != "OK": if data["status"] != "OK":
"""TODO: show some kind of error?""" """TODO: show some kind of error?"""
return render_template("index.html") return render_template("index.html")
@ -76,112 +79,104 @@ def lnurlwallet():
data = r.json() data = r.json()
with Database() as db: with Database() as db:
adminkey = encrypt(payment_hash)[0:20] adminkey = uuid.uuid4().hex
inkey = encrypt(adminkey)[0:20] inkey = uuid.uuid4().hex
thewal = encrypt(inkey)[0:20] thewal = uuid.uuid4().hex
theid = encrypt(thewal)[0:20] theid = uuid.uuid4().hex
thenme = "Bitcoin LN Wallet" thenme = DEFAULT_USER_WALLET_NAME
db.execute("INSERT INTO accounts (userhash) VALUES (?)", (theid,))
adminkey = encrypt(theid)
inkey = encrypt(adminkey)
db.execute("INSERT INTO accounts (id) VALUES (?)", (theid,))
db.execute( db.execute(
"INSERT INTO wallets (hash, name, user, adminkey, inkey) VALUES (?, ?, ?, ?, ?)", "INSERT INTO wallets (id, name, user, adminkey, inkey) VALUES (?, ?, ?, ?, ?)",
(thewal, thenme, theid, adminkey, inkey), (thewal, thenme, theid, adminkey, inkey),
) )
return render_template( return redirect(url_for("wallet", usr=theid, wal=thewal))
"lnurlwallet.html",
len=len("1"),
walnme=thenme,
walbal=withdraw_res.max_sats,
theid=theid,
thewal=thewal,
adminkey=adminkey,
inkey=inkey,
)
@app.route("/wallet") @app.route("/wallet")
def wallet(): def wallet():
theid = request.args.get("usr") usr = request.args.get("usr")
thewal = request.args.get("wal") wallet_id = request.args.get("wal")
thenme = request.args.get("nme") wallet_name = request.args.get("nme") or DEFAULT_USER_WALLET_NAME
if not thewal: # just usr: return a the first user wallet or create one if none found
return render_template("index.html") # usr and wallet_id: return that wallet or create it if it doesn't exist
# usr, wallet_id and wallet_name: same as above, but use the specified name
# usr and wallet_name: generate a wallet_id and create
# wallet_id and wallet_name: create a user, then move an existing wallet or create
# just wallet_name: create a user, then generate a wallet_id and create
# nothing: create everything
with Database() as db: with Database() as db:
user_exists = db.fetchone("SELECT * FROM accounts WHERE userhash = ?", (theid,)) # ensure this user exists
# -------------------------------
if not user_exists:
# user does not exist: create an account
# --------------------------------------
db.execute("INSERT INTO accounts (userhash) VALUES (?)", (theid,)) if not usr:
usr = uuid.uuid4().hex
return redirect(url_for("wallet", usr=usr, wal=wallet_id, nme=wallet_name))
# user exists db.execute(
# ----------- """
INSERT INTO accounts (id) VALUES (?)
ON CONFLICT (id) DO NOTHING
""",
(usr,),
)
user_wallets = db.fetchall("SELECT * FROM wallets WHERE user = ?", (theid,)) user_wallets = db.fetchall("SELECT * FROM wallets WHERE user = ?", (usr,))
if not wallet_id:
# if not given, fetch the first wallet from this user or create
# -------------------------------------------------------------
if user_wallets: if user_wallets:
wallet_id = user_wallets[0]["id"]
else:
wallet_id = uuid.uuid4().hex
db.execute(
"""
INSERT INTO wallets (id, name, user, adminkey, inkey)
VALUES (?, ?, ?, ?, ?)
""",
(wallet_id, wallet_name, usr, uuid.uuid4().hex, uuid.uuid4().hex),
)
# user has wallets return redirect(url_for("wallet", usr=usr, wal=wallet_id, nme=wallet_name))
# ----------------
wallet_row = db.fetchone( # if wallet_id is given, try to move it to this user or create
# ------------------------------------------------------------
db.execute(
"""
INSERT INTO wallets (id, name, user, adminkey, inkey)
VALUES (?, ?, ?, ?, ?)
ON CONFLICT (id) DO UPDATE SET user = ?
""",
(wallet_id, wallet_name, usr, uuid.uuid4().hex, uuid.uuid4().hex, usr),
)
# finally, get the wallet with balance and transactions
# -----------------------------------------------------
wallet = db.fetchone(
""" """
SELECT SELECT
(SELECT balance/1000 FROM balances WHERE wallet = wallets.hash), coalesce(
(SELECT balance/1000 FROM balances WHERE wallet = wallets.id),
0
) AS balance,
name, name,
adminkey, adminkey,
inkey inkey
FROM wallets FROM wallets
WHERE user = ? AND hash = ? WHERE user = ? AND id = ?
""", """,
(theid, thewal,), (usr, wallet_id),
) )
transactions = [] transactions = []
return render_template( return render_template(
"wallet.html", "wallet.html", user_wallets=user_wallets, wallet=wallet, user=usr, transactions=transactions,
thearr=user_wallets,
len=len(user_wallets),
walnme=wallet_row[1],
user=theid,
walbal=wallet_row[0],
theid=theid,
thewal=thewal,
transactions=transactions,
adminkey=wallet_row[2],
inkey=wallet_row[3],
)
# user has no wallets
# -------------------
adminkey = encrypt(theid)
inkey = encrypt(adminkey)
db.execute(
"INSERT INTO wallets (hash, name, user, adminkey, inkey) VALUES (?, ?, ?, ?, ?)",
(thewal, thenme, theid, adminkey, inkey),
)
return render_template(
"wallet.html",
len=1,
walnme=thenme,
walbal=0,
theid=theid,
thewal=thewal,
adminkey=adminkey,
inkey=inkey,
transactions=[],
) )
@ -205,12 +200,12 @@ def api_invoices():
return jsonify({"ERROR": "NO MEMO"}), 400 return jsonify({"ERROR": "NO MEMO"}), 400
with Database() as db: with Database() as db:
wallet_row = db.fetchone( wallet = db.fetchone(
"SELECT hash FROM wallets WHERE inkey = ? OR adminkey = ?", "SELECT id FROM wallets WHERE inkey = ? OR adminkey = ?",
(request.headers["Grpc-Metadata-macaroon"], request.headers["Grpc-Metadata-macaroon"],), (request.headers["Grpc-Metadata-macaroon"], request.headers["Grpc-Metadata-macaroon"],),
) )
if not wallet_row: if not wallet:
return jsonify({"ERROR": "NO KEY"}), 200 return jsonify({"ERROR": "NO KEY"}), 200
r = WALLET.create_invoice(postedjson["value"], postedjson["memo"]) r = WALLET.create_invoice(postedjson["value"], postedjson["memo"])
@ -218,10 +213,11 @@ def api_invoices():
pay_req = data["pay_req"] pay_req = data["pay_req"]
payment_hash = data["payment_hash"] payment_hash = data["payment_hash"]
amount_msat = int(postedjson["value"]) * 1000
db.execute( db.execute(
"INSERT INTO apipayments (payhash, amount, wallet, pending, memo) VALUES (?, ?, ?, true, ?)", "INSERT INTO apipayments (payhash, amount, wallet, pending, memo) VALUES (?, ?, ?, true, ?)",
(payment_hash, int(postedjson["value"]) * 1000, wallet_row[0], postedjson["memo"],), (payment_hash, amount_msat, wallet["id"], postedjson["memo"],),
) )
return jsonify({"pay_req": pay_req, "payment_hash": payment_hash}), 200 return jsonify({"pay_req": pay_req, "payment_hash": payment_hash}), 200
@ -238,39 +234,11 @@ def api_transactions():
return jsonify({"ERROR": "NO PAY REQ"}), 200 return jsonify({"ERROR": "NO PAY REQ"}), 200
with Database() as db: with Database() as db:
wallet_row = db.fetchone( wallet = db.fetchone("SELECT id FROM wallets WHERE adminkey = ?", (request.headers["Grpc-Metadata-macaroon"],))
"SELECT hash FROM wallets WHERE adminkey = ?", (request.headers["Grpc-Metadata-macaroon"],)
)
if not wallet_row: if not wallet:
return jsonify({"ERROR": "BAD AUTH"}), 200 return jsonify({"ERROR": "BAD AUTH"}), 200
# TODO: check this unused code
# move sats calculation to a helper
# ---------------------------------
"""
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
"""
# ---------------------------------
# decode the invoice # decode the invoice
invoice = bolt11.decode(data["payment_request"]) invoice = bolt11.decode(data["payment_request"])
if invoice.amount_msat == 0: if invoice.amount_msat == 0:
@ -283,13 +251,13 @@ def api_transactions():
invoice.payment_hash, invoice.payment_hash,
-int(invoice.amount_msat), -int(invoice.amount_msat),
-int(invoice.amount_msat * 0.01), -int(invoice.amount_msat * 0.01),
wallet_row[0], wallet["id"],
invoice.description, invoice.description,
), ),
) )
# check balance # check balance
balance = db.fetchone("SELECT balance/1000 FROM balances WHERE wallet = ?", (wallet_row[0],))[0] balance = db.fetchone("SELECT balance/1000 FROM balances WHERE wallet = ?", (wallet["id"],))[0]
if balance < 0: if balance < 0:
return jsonify({"ERROR": "INSUFFICIENT BALANCE"}), 403 return jsonify({"ERROR": "INSUFFICIENT BALANCE"}), 403
@ -321,24 +289,23 @@ def api_checkinvoice(payhash):
return jsonify({"ERROR": "MUST BE JSON"}), 200 return jsonify({"ERROR": "MUST BE JSON"}), 200
with Database() as db: with Database() as db:
payment_row = db.fetchone( payment = db.fetchone(
""" """
SELECT pending FROM apipayments SELECT pending FROM apipayments
INNER JOIN wallets AS w ON apipayments.wallet = w.hash INNER JOIN wallets AS w ON apipayments.wallet = w.id
WHERE payhash = ? WHERE payhash = ?
AND (w.adminkey = ? OR w.inkey = ?) AND (w.adminkey = ? OR w.inkey = ?)
""", """,
(payhash, request.headers["Grpc-Metadata-macaroon"], request.headers["Grpc-Metadata-macaroon"]), (payhash, request.headers["Grpc-Metadata-macaroon"], request.headers["Grpc-Metadata-macaroon"]),
) )
if not payment_row: if not payment:
return jsonify({"ERROR": "NO INVOICE"}), 404 return jsonify({"ERROR": "NO INVOICE"}), 404
if not payment_row[0]: # pending if not payment["pending"]: # pending
return jsonify({"PAID": "TRUE"}), 200 return jsonify({"PAID": "TRUE"}), 200
r = WALLET.get_invoice_status(payhash) r = WALLET.get_invoice_status(payhash)
if not r.ok: if not r.ok:
return jsonify({"PAID": "FALSE"}), 400 return jsonify({"PAID": "FALSE"}), 400

8
LNbits/data/schema.sql

@ -1,11 +1,11 @@
CREATE TABLE IF NOT EXISTS accounts ( CREATE TABLE IF NOT EXISTS accounts (
userhash text PRIMARY KEY, id text PRIMARY KEY,
email text, email text,
pass text pass text
); );
CREATE TABLE IF NOT EXISTS wallets ( CREATE TABLE IF NOT EXISTS wallets (
hash text PRIMARY KEY, id text PRIMARY KEY,
name text NOT NULL, name text NOT NULL,
user text NOT NULL, user text NOT NULL,
adminkey text NOT NULL, adminkey text NOT NULL,
@ -21,9 +21,7 @@ CREATE TABLE IF NOT EXISTS apipayments (
memo text memo text
); );
DROP VIEW IF EXISTS balances; CREATE VIEW IF NOT EXISTS balances AS
CREATE VIEW balances AS
SELECT wallet, coalesce(sum(s), 0) AS balance FROM ( SELECT wallet, coalesce(sum(s), 0) AS balance FROM (
SELECT wallet, sum(amount) AS s -- incoming SELECT wallet, sum(amount) AS s -- incoming
FROM apipayments FROM apipayments

1
LNbits/db.py

@ -7,6 +7,7 @@ class Database:
def __init__(self, db_path: str = DATABASE_PATH): def __init__(self, db_path: str = DATABASE_PATH):
self.path = db_path self.path = db_path
self.connection = sqlite3.connect(db_path) self.connection = sqlite3.connect(db_path)
self.connection.row_factory = sqlite3.Row
self.cursor = self.connection.cursor() self.cursor = self.connection.cursor()
def __enter__(self): def __enter__(self):

5
LNbits/helpers.py

@ -1,5 +0,0 @@
import hashlib
def encrypt(string: str):
return hashlib.sha256(string.encode()).hexdigest()

1
LNbits/settings.py

@ -14,3 +14,4 @@ WALLET = LntxbotWallet(
LNBITS_PATH = os.path.dirname(os.path.realpath(__file__)) LNBITS_PATH = os.path.dirname(os.path.realpath(__file__))
DATABASE_PATH = os.getenv("DATABASE_PATH") or os.path.join(LNBITS_PATH, "data", "database.sqlite3") DATABASE_PATH = os.getenv("DATABASE_PATH") or os.path.join(LNBITS_PATH, "data", "database.sqlite3")
DEFAULT_USER_WALLET_NAME = os.getenv("DEFAULT_USER_WALLET_NAME") or "Bitcoin LN Wallet"

55
LNbits/templates/base.html

@ -239,6 +239,61 @@
></script> ></script>
</head> </head>
<body class="skin-blue"> <body class="skin-blue">
<div class="wrapper">
<header class="main-header">
<!-- Logo -->
<a href="/" 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">
{% block messages %}{% endblock %}
</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">MENU</li>
{% block menuitems %}{% endblock %}
</ul>
</section>
<!-- /.sidebar -->
</aside>
{% block body %}{% endblock %} {% block body %}{% endblock %}
</div>
<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>
</body> </body>
</html> </html>

80
LNbits/templates/index.html

@ -1,52 +1,17 @@
<!-- @format --> <!-- @format -->
{% extends "base.html" %} {% block body %} {% extends "base.html" %} {% block menuitems %}
<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> <li>
<a href="../documentation/index.html" <a href="/"><i class="fa fa-book"></i> Home</a>
><i class="fa fa-book"></i> Home</a
>
</li> </li>
</ul> {% endblock %} {% block body %}
</section>
<!-- /.sidebar -->
</aside>
<!-- Right side column. Contains the navbar and content of the page --> <!-- Right side column. Contains the navbar and content of the page -->
<div class="content-wrapper"> <div class="content-wrapper">
<!-- Content Header (Page header) --> <!-- Content Header (Page header) -->
<section class="content-header"> <section class="content-header">
<ol class="breadcrumb"> <ol class="breadcrumb">
<li> <li>
<a href="/index.html"><i class="fa fa-dashboard"></i> Home</a> <a href="/"><i class="fa fa-dashboard"></i> Home</a>
</li> </li>
</ol> </ol>
<br /><br /> <br /><br />
@ -70,20 +35,20 @@
<small>free and open-source lightning wallet</small> <small>free and open-source lightning wallet</small>
</h1> </h1>
<p> <p>
LNbits is a simple, free and open-source lightning-network LNbits is a simple, free and open-source lightning-network wallet
wallet for bits and bobs. You can run it on your own server, or for bits and bobs. You can run it on your own server, or use this
use this one. one.
<br /><br /> <br /><br />
The wallet can be used in a variety of ways, an instant wallet The wallet can be used in a variety of ways, an instant wallet for
for LN demonstrations, a fallback wallet for the LNURL scheme, LN demonstrations, a fallback wallet for the LNURL scheme, an
an accounts system to mitigate the risk of exposing applications accounts system to mitigate the risk of exposing applications to
to your full balance. your full balance.
<br /><br /> <br /><br />
The wallet can run on top of LND, lntxbot, paywall, opennode The wallet can run on top of LND, lntxbot, paywall, opennode
<br /><br /> <br /><br />
Please note that although one of the aims of this wallet is to Please note that although one of the aims of this wallet is to
mitigate exposure of all your funds, it’s still very BETA and mitigate exposure of all your funds, it’s still very BETA and may
may in fact do the opposite! in fact do the opposite!
<br /> <br />
<a href="https://github.com/arcbtc/FOSSAW" <a href="https://github.com/arcbtc/FOSSAW"
>https://github.com/arcbtc/lnbits</a >https://github.com/arcbtc/lnbits</a
@ -111,11 +76,7 @@
required required
/> />
</div> </div>
<button <button type="button" class="btn btn-primary" onclick="newwallet()">
type="button"
class="btn btn-primary"
onclick="newwallet()"
>
Submit Submit
</button> </button>
</div> </div>
@ -129,19 +90,6 @@
</div> </div>
<!-- /.content-wrapper --> <!-- /.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>
<script> <script>
function makeid(length) { function makeid(length) {
var result = '' var result = ''

716
LNbits/templates/wallet.html

@ -1,110 +1,60 @@
{% extends "base.html" %} <!-- @format -->
{% block body %}
<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"> {% extends "base.html" %} {% block messages %}
<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"> <a href="#" class="dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-bell-o"></i> <i class="fa fa-bell-o"></i>
<span class="label label-danger">!</span> <span class="label label-danger">!</span>
</a> </a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li class="header"><b>Instant wallet, bookmark to save</b></li> <li class="header"><b>Instant wallet, bookmark to save</b></li>
<li> <li></li>
</li>
</ul>
</li>
</ul> </ul>
</div> {% endblock %} {% block menuitems %}
<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"> <li class="active treeview">
<a href="#"> <a href="#">
<i class="fa fa-bitcoin"></i> <span>Wallets</span> <i class="fa fa-bitcoin"></i> <span>Wallets</span>
<i class="fa fa-angle-left pull-right"></i> <i class="fa fa-angle-left pull-right"></i>
</a> </a>
<ul class="treeview-menu"> <ul class="treeview-menu">
{% for w in user_wallets %}
<li>
{% if len > 1 %} <a href="wallet?wal={{ w.id }}&usr={{ w.user }}"
{%for i in range(0, len)%} ><i class="fa fa-bolt"></i> {{ w.name }}</a
<li><a href="wallet?wal={{ thearr[i][0] }}&usr={{ thearr[i][4] }}" ><i class="fa fa-bolt"></i> {{ thearr[i][3] }}</a></li> >
</li>
{% endfor %} {% 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> <li><a onclick="sidebarmake()">Add a wallet +</a></li>
<div id="sidebarmake"></div> <div id="sidebarmake"></div>
</ul> </ul>
</li> </li>
<li class="treeview"> <li class="treeview">
<a href="#"> <a href="#">
<i class="fa fa-th"></i> <span>Extensions</span> <small class="label pull-right bg-green">coming soon</small> <i class="fa fa-th"></i> <span>Extensions</span>
<small class="label pull-right bg-green">coming soon</small>
</a> </a>
</li> </li>
</ul> {% endblock %} {% block body %}
</section>
<!-- /.sidebar -->
</aside>
<!-- Right side column. Contains the navbar and content of the page --> <!-- Right side column. Contains the navbar and content of the page -->
<div class="content-wrapper"> <div class="content-wrapper">
<!-- Content Header (Page header) --> <!-- Content Header (Page header) -->
<section class="content-header"> <section class="content-header">
<h1> <h1>
Wallet Wallet
<small>Control panel <div id="wonga"></div></small> <small
>Control panel
<div id="wonga"></div
></small>
</h1> </h1>
<ol class="breadcrumb"> <ol class="breadcrumb">
<li><a href="#"><i class="fa fa-dashboard"></i> Home</a></li> <li>
<a href="#"><i class="fa fa-dashboard"></i> Home</a>
</li>
<li class="active">Wallets</li> <li class="active">Wallets</li>
<li href="wallet?wal={{thewal}}" class="active">{{ walnme }}</li> <li href="wallet?wal={{ wallet.id }}" class="active">
{{ wallet.name }}
</li>
</ol> </ol>
</section> </section>
<!-- Main content --> <!-- Main content -->
@ -116,430 +66,517 @@
<a href="#" class="small-box-footer"> <a href="#" class="small-box-footer">
<div class="small-box bg-aqua"> <div class="small-box bg-aqua">
<div class="inner"> <div class="inner">
<h3><b>{{ walbal }} sats</b></h3> <h3><b>{{ wallet.balance }} sats</b></h3>
<h3>{{ walnme }}</h3> <h3>{{ wallet.name }}</h3>
</div> </div>
<div class="icon"> <div class="icon">
<i class="ion ion-flash"></i> <i class="ion ion-flash"></i>
</div> </div>
</a>
</div> </div>
</div><!-- ./col --> <!-- ./col -->
><!-- /.row -->
</div><!-- /.row -->
<div class="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">
<div class="col-sm-3"> <button onclick="receive()" class="btn btn-block btn-primary btn-lg">Receive</button></div> <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>
<div id="receive"></div> <div id="receive"></div>
<div id="sendfunds"></div> <div id="sendfunds"></div>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<div class="box"> <div class="box">
<div class="box-header"> <div class="box-header">
<h3 class="box-title">Transactions <b id="demo"></b></h3> <h3 class="box-title">Transactions <b id="demo"></b></h3>
</div>
</div><!-- /.box-header --> <!-- /.box-header -->
<div class="box-body no-padding"> <div class="box-body no-padding">
<table id="pagnation" class="table table-bordered table-striped"> <table
id="pagnation"
class="table table-bordered table-striped"
>
<tr> <tr>
<th>Memo</th> <th>Memo</th>
<th style='width: 20%'>date</th> <th style="width: 20%">date</th>
<th style='width: 20%'>amount</th> <th style="width: 20%">amount</th>
</tr> </tr>
<tbody id="transactions"> <tbody id="transactions"></tbody>
</table>
</div>
<!-- /.box-body -->
</tbody></table> </div>
</div><!-- /.box-body --> <!-- /.box -->
</div><!-- /.box -->
</div> </div>
</div> </div>
<div id="satschart"></div> <div id="satschart"></div>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<div class="box box-solid"> <div class="box box-solid">
<div class="box-header with-border"> <div class="box-header with-border"></div>
<!-- /.box-header -->
</div><!-- /.box-header -->
<div class="box-body"> <div class="box-body">
<div class="box-group" id="accordion"> <div class="box-group" id="accordion">
<!-- we are adding the .panel class so bootstrap.js collapse plugin detects it --> <!-- we are adding the .panel class so bootstrap.js collapse plugin detects it -->
<div class="panel box box-primary"> <div class="panel box box-primary">
<div class="box-header with-border"> <div class="box-header with-border">
<h4 class="box-title"> <h4 class="box-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapseThree" class="collapsed" aria-expanded="false"> <a
Wallet "{{ walnme }}" API info data-toggle="collapse"
data-parent="#accordion"
href="#collapseThree"
class="collapsed"
aria-expanded="false"
>
Wallet "{{ wallet.name }}" API info
</a> </a>
</h4> </h4>
</div> </div>
<div id="collapseThree" class="panel-collapse collapse" aria-expanded="false"> <div
<div class="box-body" style='word-wrap: break-word;'> id="collapseThree"
<b>Admin key: </b><i>{{ adminkey }}</i><br/> class="panel-collapse collapse"
<b>Invoice/Read key: </b><i>{{ inkey }}</i><br/> aria-expanded="false"
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/> <div class="box-body" style="word-wrap: break-word;">
Returns <code>{"pay_req": string,"pay_id": string} </code><br/> <b>Admin key: </b><i>{{ wallet.adminkey }}</i><br />
*payment will not register in the wallet until the "check invoice" endpoint is used<br/><br/> <b>Invoice/Read key: </b><i>{{ wallet.inkey }}</i
><br />
Generate an invoice:<br /><code
>POST /v1/invoices</code
><br />Header
<code
>{"Grpc-Metadata-macaroon": "<i
>{{ wallet.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 invoice:<br />
Check an invoice:<br/><code>GET /v1/invoice/*payment_hash*</code><br/>Header <code>{"Grpc-Metadata-macaroon": "<i>{{ inkey }}</i>"}</code><br/> Check an invoice:<br /><code
>GET /v1/invoice/*payment_hash*</code
Returns <code>{"PAID": "TRUE"}/{"PAID": "FALSE"} </code><br/> ><br />Header
<code
>{"Grpc-Metadata-macaroon": "<i>{{ wallet.inkey }}</i
>"}</code
><br />
Returns
<code>{"PAID": "TRUE"}/{"PAID": "FALSE"} </code><br />
*if using LNTXBOT return will hang until paid<br /><br /> *if using LNTXBOT return will hang until paid<br /><br />
</div> </div>
</div> </div>
</div> </div>
<div class="panel box box-danger"> <div class="panel box box-danger">
<div class="box-header with-border"> <div class="box-header with-border">
<h4 class="box-title"> <h4 class="box-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapseTwo" class="collapsed" aria-expanded="false"> <a
data-toggle="collapse"
data-parent="#accordion"
href="#collapseTwo"
class="collapsed"
aria-expanded="false"
>
Delete wallet Delete wallet
</a> </a>
</h4> </h4>
</div> </div>
<div id="collapseTwo" class="panel-collapse collapse" aria-expanded="false"> <div
id="collapseTwo"
class="panel-collapse collapse"
aria-expanded="false"
>
<div class="box-body"> <div class="box-body">
This whole wallet will be deleted, the funds will be
This whole wallet will be deleted, the funds will be UNRECOVERABLE <br/><br/><button class="btn btn-danger" onclick="deletewallet()">Delete wallet</button> UNRECOVERABLE <br /><br /><button
class="btn btn-danger"
onclick="deletewallet()"
>
Delete wallet
</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div><!-- /.box-body --> </div>
</div><!-- /.box --> <!-- /.box-body -->
</div>
<!-- /.box -->
</div> </div>
</div> </div>
</section><!-- /.content --> <!-- /.content -->
</a>
</div>
</div>
</section>
</div><!-- /.content-wrapper -->
<footer class="main-footer">
<div class="pull-right hidden-xs">
<b>BETA</b>
</div> </div>
<strong>Learn more about LNbits <a href="https://github.com/arcbtc/lnbits">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') }}">
<script> <script>
var inmacaroon = '{{ wallet.inkey }}'
var inmacaroon = "{{ inkey }}"; var adminmacaroon = '{{ wallet.inkey }}'
var adminmacaroon = "{{ inkey }}"; var thehash = ''
var thehash = ""; var theinvoice = ''
var theinvoice = ""; var outamount = ''
var outamount = ""; var outmemo = ''
var outmemo = "";
//API CALLS //API CALLS
function postAjax(url, data, thekey, success) { function postAjax(url, data, thekey, success) {
var params = typeof data == 'string' ? data : Object.keys(data).map( var params =
function(k){ return encodeURIComponent(k) + '=' + encodeURIComponent(data[k]) }).join('&'); typeof data == 'string'
var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"); ? data
xhr.open('POST', url); : 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() { xhr.onreadystatechange = function() {
if (xhr.readyState>3 && xhr.status==200) { success(xhr.responseText); } 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); xhr.setRequestHeader('Grpc-Metadata-macaroon', thekey)
return xhr; xhr.setRequestHeader('Content-Type', 'application/json')
xhr.send(params)
return xhr
} }
function getAjax(url, thekey, success) { function getAjax(url, thekey, success) {
var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"); var xhr = window.XMLHttpRequest
xhr.open('GET', url, true); ? new XMLHttpRequest()
: new ActiveXObject('Microsoft.XMLHTTP')
xhr.open('GET', url, true)
xhr.onreadystatechange = function() { xhr.onreadystatechange = function() {
if (xhr.readyState>3 && xhr.status==200) { success(xhr.responseText); } if (xhr.readyState > 3 && xhr.status == 200) {
}; success(xhr.responseText)
xhr.setRequestHeader('Grpc-Metadata-macaroon', thekey); }
xhr.setRequestHeader('Content-Type', 'application/json'); }
xhr.send(); xhr.setRequestHeader('Grpc-Metadata-macaroon', thekey)
return xhr; xhr.setRequestHeader('Content-Type', 'application/json')
xhr.send()
return xhr
} }
function sendfundsinput() { function sendfundsinput() {
document.getElementById("sendfunds").innerHTML = "<br/><br/><div class='row'><div class='col-md-4'>" + 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>" + "<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'>" + "<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()'>" + "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/>"; 'Use camera to scan an invoice</button></div></div><br/><br/>'
document.getElementById("receive").innerHTML = ""; document.getElementById('receive').innerHTML = ''
} }
function sendfundspaste() { function sendfundspaste() {
invoice = document.getElementById("pasteinvoice").value; invoice = document.getElementById('pasteinvoice').value
theinvoice = decode(invoice); theinvoice = decode(invoice)
outmemo = theinvoice.data.tags[1].value; outmemo = theinvoice.data.tags[1].value
outamount = Number(theinvoice.human_readable_part.amount) /1000; outamount = Number(theinvoice.human_readable_part.amount) / 1000
if (outamount > Number("{{ walbal }}")){ if (outamount > Number('{{ wallet.balance }}')) {
document.getElementById("sendfunds").innerHTML = "<div class='row'><div class='col-md-6'>"+ document.getElementById('sendfunds').innerHTML =
"<div class='row'><div class='col-md-6'>" +
"<h3><b style='color:red;'>Not enough funds!</b></h3>" + "<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>" + "<button style='margin-left:20px;' type='submit' class='btn btn-primary' onclick='cancelsend()'>Continue</button>" +
"</br/></br/></div></div>"; '</br/></br/></div></div>'
} } else {
else{ document.getElementById('sendfunds').innerHTML =
document.getElementById("sendfunds").innerHTML = "<div class='row'><div class='col-md-6'>"+ "<div class='row'><div class='col-md-6'>" +
"<h3><b>Invoice details</b></br/>Amount: " + outamount + "<br/>Memo: " + outmemo + "</h3>" + '<h3><b>Invoice details</b></br/>Amount: ' +
"<h4 style='word-wrap: break-word;'>" + invoice + "</h4>"+ outamount +
"<button type='submit' class='btn btn-primary' onclick='sendfunds(" + JSON.stringify(invoice) + ")'>Send funds</button>"+ '<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>" + "<button style='margin-left:20px;' type='submit' class='btn btn-primary' onclick='cancelsend()'>Cancel payment</button>" +
"</br/></br/></div></div>"; '</br/></br/></div></div>'
} }
} }
function receive() { function receive() {
document.getElementById("receive").innerHTML = "<br/><div class='row'><div id='QRCODE'>" + 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='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='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 class='col-sm-2'><input type='button' id='getinvoice' onclick='received()' class='btn btn-primary' value='Create invoice' /></div>" +
"</div></div><br/>"; '</div></div><br/>'
document.getElementById("sendfunds").innerHTML = ""; document.getElementById('sendfunds').innerHTML = ''
} }
function received() { function received() {
memo = document.getElementById("memo").value; memo = document.getElementById('memo').value
amount = document.getElementById("amount").value; amount = document.getElementById('amount').value
postAjax('/v1/invoices', JSON.stringify({'value' : amount, 'memo' : memo}), '{{ inkey }}', function(data){ postAjax(
theinvoice = JSON.parse(data).pay_req; '/v1/invoices',
thehash = JSON.parse(data).payment_hash; JSON.stringify({value: amount, memo: memo}),
document.getElementById("QRCODE").innerHTML = "<div class='col-md-4'><div class='box'><div class='box-header'>" + '{{ wallet.inkey }}',
"<center><a href='lightning:" + theinvoice + "'><div id='qrcode'></div></a>" + function(data) {
"<p style='word-wrap: break-word;'>" + theinvoice + "</p></div></div></div></center>"; theinvoice = JSON.parse(data).pay_req
thehash = JSON.parse(data).payment_hash
new QRCode(document.getElementById("qrcode"), { 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, text: theinvoice,
width: 300, width: 300,
height: 300, height: 300,
colorDark : "#000000", colorDark: '#000000',
colorLight : "#ffffff", colorLight: '#ffffff',
correctLevel: QRCode.CorrectLevel.M correctLevel: QRCode.CorrectLevel.M
}); })
getAjax('/v1/invoice/'+ thehash, '{{ inkey }}', function(datab){ getAjax('/v1/invoice/' + thehash, '{{ wallet.inkey }}', function(datab) {
console.log(JSON.parse(datab).PAID); console.log(JSON.parse(datab).PAID)
if (JSON.parse(datab).PAID == 'TRUE') { if (JSON.parse(datab).PAID == 'TRUE') {
window.location.href = "wallet?wal=" + "{{thewal}}" + "&usr=" + "{{theid}}"; window.location.href =
'wallet?wal=' + '{{ wallet.id }}' + '&usr=' + '{{ user }}'
} }
})
}); }
}); )
} }
function cancelsend() { function cancelsend() {
window.location.href = "wallet?wal=" + "{{thewal}}" + "&usr=" + "{{theid}}"; window.location.href =
'wallet?wal=' + '{{ wallet.id }}' + '&usr=' + '{{ user }}'
} }
function sendfunds(invoice) { function sendfunds(invoice) {
var url = '/v1/channels/transactions'; var url = '/v1/channels/transactions'
postAjax(url, JSON.stringify({'payment_request' : invoice}), '{{ adminkey }}', function(data){ postAjax(
thehash = JSON.parse(data).payment_hash; url,
console.log(JSON.parse(data)); JSON.stringify({payment_request: invoice}),
'{{ wallet.adminkey }}',
function(data) {
thehash = JSON.parse(data).payment_hash
console.log(JSON.parse(data))
if (JSON.parse(data).PAID == 'TRUE') { if (JSON.parse(data).PAID == 'TRUE') {
window.location.href = "wallet?wal=" + "{{thewal}}" + "&usr=" + "{{theid}}"; window.location.href =
'wallet?wal=' + '{{ wallet.id }}' + '&usr=' + '{{ user }}'
}
} }
}); )
} }
function scanQRsend() { function scanQRsend() {
document.getElementById("sendfunds").innerHTML = "<br/><br/><div class='row'><div class='col-md-4'>" + 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>" + "<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>" + "<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/>"; "<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 video = document.createElement('video')
var canvasElement = document.getElementById("canvas"); var canvasElement = document.getElementById('canvas')
var canvas = canvasElement.getContext("2d"); var canvas = canvasElement.getContext('2d')
var loadingMessage = document.getElementById("loadingMessage"); var loadingMessage = document.getElementById('loadingMessage')
var outputContainer = document.getElementById("output"); var outputContainer = document.getElementById('output')
var outputMessage = document.getElementById("outputMessage"); var outputMessage = document.getElementById('outputMessage')
var outputData = document.getElementById("outputData"); var outputData = document.getElementById('outputData')
function drawLine(begin, end, color) { function drawLine(begin, end, color) {
canvas.beginPath(); canvas.beginPath()
canvas.moveTo(begin.x, begin.y); canvas.moveTo(begin.x, begin.y)
canvas.lineTo(end.x, end.y); canvas.lineTo(end.x, end.y)
canvas.lineWidth = 4; canvas.lineWidth = 4
canvas.strokeStyle = color; canvas.strokeStyle = color
canvas.stroke(); canvas.stroke()
} }
// Use facingMode: environment to attemt to get the front camera on phones // Use facingMode: environment to attemt to get the front camera on phones
navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } }).then(function(stream) { navigator.mediaDevices
video.srcObject = stream; .getUserMedia({video: {facingMode: 'environment'}})
video.setAttribute("playsinline", true); // required to tell iOS safari we don't want fullscreen .then(function(stream) {
video.play(); video.srcObject = stream
requestAnimationFrame(tick); video.setAttribute('playsinline', true) // required to tell iOS safari we don't want fullscreen
}); video.play()
requestAnimationFrame(tick)
})
function tick() { function tick() {
loadingMessage.innerText = "⌛ Loading video..." loadingMessage.innerText = '⌛ Loading video...'
if (video.readyState === video.HAVE_ENOUGH_DATA) { if (video.readyState === video.HAVE_ENOUGH_DATA) {
loadingMessage.hidden = true; loadingMessage.hidden = true
canvasElement.hidden = false; canvasElement.hidden = false
outputContainer.hidden = false; outputContainer.hidden = false
canvasElement.height = video.videoHeight; canvasElement.height = video.videoHeight
canvasElement.width = video.videoWidth; canvasElement.width = video.videoWidth
canvas.drawImage(video, 0, 0, canvasElement.width, canvasElement.height); canvas.drawImage(video, 0, 0, canvasElement.width, canvasElement.height)
var imageData = canvas.getImageData(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, { var code = jsQR(imageData.data, imageData.width, imageData.height, {
inversionAttempts: "dontInvert", inversionAttempts: 'dontInvert'
}); })
if (code) { if (code) {
drawLine(code.location.topLeftCorner, code.location.topRightCorner, "#FF3B58"); drawLine(
drawLine(code.location.topRightCorner, code.location.bottomRightCorner, "#FF3B58"); code.location.topLeftCorner,
drawLine(code.location.bottomRightCorner, code.location.bottomLeftCorner, "#FF3B58"); code.location.topRightCorner,
drawLine(code.location.bottomLeftCorner, code.location.topLeftCorner, "#FF3B58"); '#FF3B58'
outputMessage.hidden = true; )
outputData.parentElement.hidden = false; drawLine(
outputData.innerText = JSON.stringify(code.data); code.location.topRightCorner,
theinvoice = decode(code.data); code.location.bottomRightCorner,
outmemo = theinvoice.data.tags[1].value; '#FF3B58'
outamount = Number(theinvoice.human_readable_part.amount) /1000; )
if (outamount > Number("{{ walbal }}")){ drawLine(
document.getElementById("sendfunds").innerHTML = "<div class='row'><div class='col-md-6'>"+ 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('{{ wallet.balance }}')) {
document.getElementById('sendfunds').innerHTML =
"<div class='row'><div class='col-md-6'>" +
"<h3><b style='color:red;'>Not enough funds!</b></h3>" + "<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>" + "<button style='margin-left:20px;' type='submit' class='btn btn-primary' onclick='cancelsend()'>Continue</button>" +
"</br/></br/></div></div>"; '</br/></br/></div></div>'
} } else {
else{ document.getElementById('sendfunds').innerHTML =
"<div class='row'><div class='col-md-6'>" +
'<h3><b>Invoice details</b></br/>Amount: ' +
document.getElementById("sendfunds").innerHTML = "<div class='row'><div class='col-md-6'>"+ outamount +
"<h3><b>Invoice details</b></br/>Amount: " + outamount + "<br/>Memo: " + outmemo + "</h3>" + '<br/>Memo: ' +
"<h4 style='word-wrap: break-word;'>" + JSON.stringify(code.data) + "</h4>"+ outmemo +
"<button type='submit' class='btn btn-primary' onclick='sendfunds(" + JSON.stringify(code.data) + ")'>Send funds</button>"+ '</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>" + "<button style='margin-left:20px;' type='submit' class='btn btn-primary' onclick='cancelsend()'>Cancel payment</button>" +
"</br/></br/></div></div>"; '</br/></br/></div></div>'
} }
} else {
outputMessage.hidden = false
outputData.parentElement.hidden = true
} }
else {
outputMessage.hidden = false;
outputData.parentElement.hidden = true;
} }
requestAnimationFrame(tick)
} }
requestAnimationFrame(tick);
} }
}
function deletewallet() { function deletewallet() {
var urll = "deletewallet?wal={{ thewal }}&usr={{ theid }}"; var urll = 'deletewallet?wal={{ wallet.id }}&usr={{ user }}'
window.location.href = urll; window.location.href = urll
} }
function sidebarmake() { function sidebarmake() {
document.getElementById("sidebarmake").innerHTML = "<li><div class='form-group'>"+ 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>" + "<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>" + "<button style='width:30%;float:left;' type='button' class='btn btn-primary' onclick='newwallet()'>Submit</button>" +
"</div></li><br/><br/>"; '</div></li><br/><br/>'
} }
function makeid(length) { function makeid(length) {
var result = ''; var result = ''
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; var characters =
var charactersLength = characters.length; 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
var charactersLength = characters.length
for (var i = 0; i < length; i++) { for (var i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength)); result += characters.charAt(Math.floor(Math.random() * charactersLength))
} }
return result; return result
} }
function newwallet() { function newwallet() {
walname = document.getElementById("walname").value; walname = document.getElementById('walname').value
window.location.href = "wallet?usr=" + "{{ theid }}" + "&wal=" + makeid(40) + "&nme=" + walname; window.location.href =
'wallet?usr=' + '{{ user }}' + '&wal=' + makeid(40) + '&nme=' + walname
} }
var trans = '{{ transactions }}'
var transac = trans.substr(1)
var trans = "{{ transactions }}";
var transac = trans.substr(1);
//console.log(trans); //console.log(trans);
if(transac != ""){ if (transac != '') {
var transactions = ""; var transactions = ''
var linechart = []; var linechart = []
var tran = transac.split("!"); var tran = transac.split('!')
tran.shift(); tran.shift()
console.log(tran); console.log(tran)
for (var i = 0; i < tran.length; i++) { for (var i = 0; i < tran.length; i++) {
rects = tran[i].split(","); rects = tran[i].split(',')
rectstime = String(rects[1]).split("."); rectstime = String(rects[1]).split('.')
var datime = convertTimestamp(rectstime[0]) var datime = convertTimestamp(rectstime[0])
//Make the transactions table //Make the transactions table
transactions += "<tr><td style='width: 50%'>" + rects[0] + "</td><td>" + datime + "</td><td>" + rects[2] + "</td></tr>"; transactions +=
"<tr><td style='width: 50%'>" +
rects[0] +
'</td><td>' +
datime +
'</td><td>' +
rects[2] +
'</td></tr>'
//Make the line chart //Make the line chart
if (i == 0) { if (i == 0) {
linechart.push({y: datime, item1: rects[2]}); linechart.push({y: datime, item1: rects[2]})
} } else {
else{ linechart.push({y: datime, item1: rects[3]})
linechart.push({y: datime, item1: rects[3]});
} }
} }
document.getElementById("transactions").innerHTML = transactions; document.getElementById('transactions').innerHTML = transactions
if(linechart[0] != ""){ if (linechart[0] != '') {
document.getElementById("satschart").innerHTML = "<div class='row'><div class='col-md-6'><div class='box box-info'><div class='box-header'>" + 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'>" + "<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>"; "<div class='chart' id='line-chart' style='height: 300px;'></div></div></div></div></div>"
} }
} }
console.log(linechart); console.log(linechart)
var data = linechart var data = linechart
var line = new Morris.Line({ var line = new Morris.Line({
element: 'line-chart', element: 'line-chart',
@ -550,8 +587,7 @@ console.log(linechart);
labels: ['Item 1'], labels: ['Item 1'],
lineColors: ['#3c8dbc'], lineColors: ['#3c8dbc'],
hideHover: 'auto' hideHover: 'auto'
}); })
function convertTimestamp(timestamp) { function convertTimestamp(timestamp) {
var d = new Date(timestamp * 1000), var d = new Date(timestamp * 1000),
@ -562,10 +598,10 @@ function convertTimestamp(timestamp) {
h = hh, h = hh,
min = ('0' + d.getMinutes()).slice(-2), min = ('0' + d.getMinutes()).slice(-2),
ampm = 'AM', ampm = 'AM',
time; time
time = yyyy + '-' + mm + '-' + dd + ' ' + h + ':' + min; time = yyyy + '-' + mm + '-' + dd + ' ' + h + ':' + min
return time; return time
} }
</script> </script>
{% endblock %} {% endblock %}

Loading…
Cancel
Save