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
This commit is contained in:
parent
1cee6dad42
commit
14dfa9ecc6
8 changed files with 796 additions and 795 deletions
|
|
@ -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(
|
||||||
|
"""
|
||||||
|
UPDATE wallets AS w SET
|
||||||
|
user = 'del:' || w.user,
|
||||||
|
adminkey = 'del:' || w.adminkey,
|
||||||
|
inkey = 'del:' || w.inkey
|
||||||
|
WHERE id = ? AND user = ?
|
||||||
|
""",
|
||||||
|
(thewal, theid),
|
||||||
|
)
|
||||||
|
|
||||||
if not wallet_row:
|
next_wallet = db.fetchone("SELECT hash FROM wallets WHERE user = ?", (theid,))
|
||||||
return render_template("index.html")
|
if next_wallet:
|
||||||
|
return redirect(url_for("wallet", usr=theid, wal=next_wallet[0]))
|
||||||
db.execute("UPDATE wallets SET user = ? WHERE hash = ?", (f"del{wallet_row[4]}", wallet_row[0]))
|
|
||||||
db.execute("UPDATE wallets SET adminkey = ? WHERE hash = ?", (f"del{wallet_row[5]}", wallet_row[0]))
|
|
||||||
db.execute("UPDATE wallets SET inkey = ? WHERE hash = ?", (f"del{wallet_row[6]}", wallet_row[0]))
|
|
||||||
|
|
||||||
user_wallets = db.fetchall("SELECT * FROM wallets WHERE user = ?", (wallet_row[4],))
|
|
||||||
if user_wallets:
|
|
||||||
return render_template("deletewallet.html", theid=user_wallets[0][4], thewal=user_wallets[0][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:
|
if not usr:
|
||||||
# user does not exist: create an account
|
usr = uuid.uuid4().hex
|
||||||
# --------------------------------------
|
return redirect(url_for("wallet", usr=usr, wal=wallet_id, nme=wallet_name))
|
||||||
|
|
||||||
db.execute("INSERT INTO accounts (userhash) VALUES (?)", (theid,))
|
db.execute(
|
||||||
|
"""
|
||||||
|
INSERT INTO accounts (id) VALUES (?)
|
||||||
|
ON CONFLICT (id) DO NOTHING
|
||||||
|
""",
|
||||||
|
(usr,),
|
||||||
|
)
|
||||||
|
|
||||||
# user exists
|
user_wallets = db.fetchall("SELECT * FROM wallets WHERE user = ?", (usr,))
|
||||||
# -----------
|
|
||||||
|
|
||||||
user_wallets = db.fetchall("SELECT * FROM wallets WHERE user = ?", (theid,))
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
import hashlib
|
|
||||||
|
|
||||||
|
|
||||||
def encrypt(string: str):
|
|
||||||
return hashlib.sha256(string.encode()).hexdigest()
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -1,52 +1,17 @@
|
||||||
<!-- @format -->
|
<!-- @format -->
|
||||||
|
|
||||||
{% extends "base.html" %} {% block body %}
|
{% extends "base.html" %} {% block menuitems %}
|
||||||
<div class="wrapper">
|
<li>
|
||||||
<header class="main-header">
|
<a href="/"><i class="fa fa-book"></i> Home</a>
|
||||||
<!-- Logo -->
|
</li>
|
||||||
<a href="index.html" class="logo"><b>LN</b>bits</a>
|
{% endblock %} {% block body %}
|
||||||
<!-- Header Navbar: style can be found in header.less -->
|
<!-- Right side column. Contains the navbar and content of the page -->
|
||||||
<nav class="navbar navbar-static-top" role="navigation">
|
<div class="content-wrapper">
|
||||||
<!-- 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) -->
|
<!-- 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>
|
||||||
|
|
@ -126,21 +87,8 @@
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<!-- /.content -->
|
<!-- /.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>
|
</div>
|
||||||
|
<!-- /.content-wrapper -->
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function makeid(length) {
|
function makeid(length) {
|
||||||
|
|
|
||||||
|
|
@ -1,110 +1,60 @@
|
||||||
{% extends "base.html" %}
|
<!-- @format -->
|
||||||
|
|
||||||
{% block body %}
|
{% extends "base.html" %} {% block messages %}
|
||||||
<div class="wrapper">
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||||
|
|
||||||
<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>
|
<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>
|
||||||
</ul>
|
{% endblock %} {% block menuitems %}
|
||||||
</li>
|
<li class="active treeview">
|
||||||
</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="#">
|
<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>
|
>
|
||||||
{%endfor%}
|
</li>
|
||||||
{% else %}
|
{% endfor %}
|
||||||
<li><a href="wallet?wal={{ thewal }}&usr={{ theid }}" ><i class="fa fa-bolt"></i> {{ walnme }}</a></li>
|
<li><a onclick="sidebarmake()">Add a wallet +</a></li>
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<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 %}
|
||||||
|
<!-- Right side column. Contains the navbar and content of the page -->
|
||||||
|
<div class="content-wrapper">
|
||||||
</section>
|
|
||||||
<!-- /.sidebar -->
|
|
||||||
</aside>
|
|
||||||
|
|
||||||
<!-- Right side column. Contains the navbar and content of the page -->
|
|
||||||
<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
|
||||||
|
><br />Header
|
||||||
|
<code
|
||||||
|
>{"Grpc-Metadata-macaroon": "<i>{{ wallet.inkey }}</i
|
||||||
|
>"}</code
|
||||||
|
><br />
|
||||||
|
|
||||||
Returns <code>{"PAID": "TRUE"}/{"PAID": "FALSE"} </code><br/>
|
Returns
|
||||||
*if using LNTXBOT return will hang until paid<br/><br/>
|
<code>{"PAID": "TRUE"}/{"PAID": "FALSE"} </code><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>
|
||||||
|
<!-- /.box-body -->
|
||||||
|
</div>
|
||||||
|
<!-- /.box -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
<!-- /.content -->
|
||||||
|
</a>
|
||||||
</div>
|
|
||||||
</div><!-- /.box-body -->
|
|
||||||
</div><!-- /.box -->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
</section><!-- /.content -->
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</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/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 adminmacaroon = '{{ wallet.inkey }}'
|
||||||
|
var thehash = ''
|
||||||
|
var theinvoice = ''
|
||||||
|
var outamount = ''
|
||||||
|
var outmemo = ''
|
||||||
|
|
||||||
var inmacaroon = "{{ inkey }}";
|
//API CALLS
|
||||||
var adminmacaroon = "{{ inkey }}";
|
|
||||||
var thehash = "";
|
|
||||||
var theinvoice = "";
|
|
||||||
var outamount = "";
|
|
||||||
var outmemo = "";
|
|
||||||
|
|
||||||
|
function postAjax(url, data, thekey, success) {
|
||||||
//API CALLS
|
var params =
|
||||||
|
typeof data == 'string'
|
||||||
function postAjax(url, data, thekey, success) {
|
? data
|
||||||
var params = typeof data == 'string' ? data : Object.keys(data).map(
|
: Object.keys(data)
|
||||||
function(k){ return encodeURIComponent(k) + '=' + encodeURIComponent(data[k]) }).join('&');
|
.map(function(k) {
|
||||||
var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
|
return encodeURIComponent(k) + '=' + encodeURIComponent(data[k])
|
||||||
xhr.open('POST', url);
|
})
|
||||||
|
.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) {
|
||||||
|
var xhr = window.XMLHttpRequest
|
||||||
function getAjax(url, thekey,success) {
|
? new XMLHttpRequest()
|
||||||
var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
|
: new ActiveXObject('Microsoft.XMLHTTP')
|
||||||
xhr.open('GET', url, true);
|
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 {
|
||||||
|
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>'
|
||||||
}
|
}
|
||||||
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 =
|
||||||
function receive() {
|
"<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
|
||||||
|
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"), {
|
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() {
|
||||||
});
|
window.location.href =
|
||||||
}
|
'wallet?wal=' + '{{ wallet.id }}' + '&usr=' + '{{ user }}'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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(){
|
function sendfunds(invoice) {
|
||||||
document.getElementById("sendfunds").innerHTML = "<br/><br/><div class='row'><div class='col-md-4'>" +
|
var url = '/v1/channels/transactions'
|
||||||
|
postAjax(
|
||||||
|
url,
|
||||||
|
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') {
|
||||||
|
window.location.href =
|
||||||
|
'wallet?wal=' + '{{ wallet.id }}' + '&usr=' + '{{ user }}'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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>" +
|
"<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 {
|
||||||
|
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{
|
} else {
|
||||||
|
outputMessage.hidden = false
|
||||||
|
outputData.parentElement.hidden = true
|
||||||
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 {
|
requestAnimationFrame(tick)
|
||||||
outputMessage.hidden = false;
|
|
||||||
outputData.parentElement.hidden = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
requestAnimationFrame(tick);
|
|
||||||
|
function deletewallet() {
|
||||||
|
var urll = 'deletewallet?wal={{ wallet.id }}&usr={{ user }}'
|
||||||
|
window.location.href = urll
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
function sidebarmake() {
|
||||||
|
document.getElementById('sidebarmake').innerHTML =
|
||||||
|
"<li><div class='form-group'>" +
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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>" +
|
"<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) {
|
|
||||||
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() {
|
function makeid(length) {
|
||||||
walname = document.getElementById("walname").value;
|
var result = ''
|
||||||
window.location.href = "wallet?usr=" + "{{ theid }}" + "&wal=" + makeid(40) + "&nme=" + walname;
|
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=' + '{{ user }}' + '&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()
|
||||||
var trans = "{{ transactions }}";
|
console.log(tran)
|
||||||
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++) {
|
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,10 +587,9 @@ 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),
|
||||||
yyyy = d.getFullYear(),
|
yyyy = d.getFullYear(),
|
||||||
mm = ('0' + (d.getMonth() + 1)).slice(-2),
|
mm = ('0' + (d.getMonth() + 1)).slice(-2),
|
||||||
|
|
@ -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…
Add table
Add a link
Reference in a new issue