mirror of https://github.com/lukechilds/lnbits.git
Browse Source
- extensions are now blueprints: keep views, templastes and statics in the same folder - increase app security using `flask-talisman` - whenever possible use {{ url_for }} for links between pages - remove references to non-existing JavaScript code - add missing favicon.icofee_issues
29 changed files with 540 additions and 1114 deletions
@ -0,0 +1,8 @@ |
|||
from flask import Blueprint |
|||
|
|||
|
|||
core_app = Blueprint("core", __name__, template_folder="templates") |
|||
|
|||
|
|||
from .views_api import * # noqa |
|||
from .views import * # noqa |
After Width: | Height: | Size: 23 KiB |
@ -0,0 +1,14 @@ |
|||
from flask import render_template, send_from_directory |
|||
from os import path |
|||
|
|||
from lnbits.core import core_app |
|||
|
|||
|
|||
@core_app.route("/favicon.ico") |
|||
def favicon(): |
|||
return send_from_directory(path.join(core_app.root_path, "static"), "favicon.ico") |
|||
|
|||
|
|||
@core_app.route("/") |
|||
def home(): |
|||
return render_template("index.html") |
@ -1,73 +0,0 @@ |
|||
|
|||
 |
|||
# LNbits |
|||
Simple free and open-source Python lightning-network wallet/accounts system. Use https://lnbits.com, or run your own LNbits server! |
|||
|
|||
LNbits is a very simple server that sits on top of a funding source, and can be used as: |
|||
* Accounts system to mitigate the risk of exposing applications to your full balance, via unique API keys for each wallet! |
|||
* Fallback wallet for the LNURL scheme |
|||
* Instant wallet for LN demonstrations |
|||
|
|||
The wallet can run on top of any lightning-network funding source such as LND, lntxbot, paywall, opennode, etc. This first BETA release runs on top of lntxbot, other releases coming soon, although if you are impatient, it could be ported to other funding sources relatively easily. Contributors very welcome :) |
|||
|
|||
LNbits is still in BETA. Please report any vulnerabilities responsibly |
|||
## LNbits as an account system |
|||
LNbits is packaged with tools to help manage funds, such as a table of transactions, line chart of spending, export to csv + more to come.. |
|||
|
|||
|
|||
 |
|||
|
|||
Each wallet also comes with its own API keys, to help partition the exposure of your funding source. |
|||
|
|||
(LNbits M5StackSats available here https://github.com/arcbtc/M5StackSats) |
|||
|
|||
 |
|||
|
|||
## LNbits as an LNURL-withdraw fallback |
|||
LNURL has a fallback scheme, so if scanned by a regular QR code reader it can default to a URL. LNbits exploits this to generate an instant wallet using the LNURL-withdraw. |
|||
|
|||
 |
|||
https://github.com/btcontract/lnurl-rfc/blob/master/spec.md |
|||
|
|||
Adding **/lnurl?lightning="LNURL-WITHDRAW"** will trigger a withdraw that builds an LNbits wallet. |
|||
Example use would be an ATM, which utilises LNURL, if the user scans the QR with a regular QR code scanner app, they will stilll be able to access the funds. |
|||
|
|||
 |
|||
|
|||
## LNbits as an insta-wallet |
|||
Wallets can be easily generated and given out to people at events (one click multi-wallet generation to be added soon). |
|||
"Go to this website", has a lot less friction than "Download this app". |
|||
|
|||
 |
|||
|
|||
# Running LNbits locally |
|||
Download this repo |
|||
|
|||
LNbits uses [Flask](http://flask.pocoo.org/). |
|||
Feel free to contribute to the project. |
|||
|
|||
Application dependencies |
|||
------------------------ |
|||
The application uses [Pipenv][pipenv] to manage Python packages. |
|||
While in development, you will need to install all dependencies: |
|||
|
|||
$ pipenv shell |
|||
$ pipenv install --dev |
|||
|
|||
You will need to set the variables in .env.example, and rename the file to .env |
|||
|
|||
 |
|||
|
|||
Running the server |
|||
------------------ |
|||
|
|||
$ flask run |
|||
|
|||
There is an environment variable called `FLASK_ENV` that has to be set to `development` |
|||
if you want to run Flask in debug mode with autoreload |
|||
|
|||
[pipenv]: https://docs.pipenv.org/#install-pipenv-today |
|||
|
|||
# Tip me |
|||
If you like this project and might even use or extend it, why not send some tip love! |
|||
https://paywall.link/to/f4e4e |
@ -1,73 +0,0 @@ |
|||
|
|||
 |
|||
# LNbits |
|||
Simple free and open-source Python lightning-network wallet/accounts system. Use https://lnbits.com, or run your own LNbits server! |
|||
|
|||
LNbits is a very simple server that sits on top of a funding source, and can be used as: |
|||
* Accounts system to mitigate the risk of exposing applications to your full balance, via unique API keys for each wallet! |
|||
* Fallback wallet for the LNURL scheme |
|||
* Instant wallet for LN demonstrations |
|||
|
|||
The wallet can run on top of any lightning-network funding source such as LND, lntxbot, paywall, opennode, etc. This first BETA release runs on top of lntxbot, other releases coming soon, although if you are impatient, it could be ported to other funding sources relatively easily. Contributors very welcome :) |
|||
|
|||
LNbits is still in BETA. Please report any vulnerabilities responsibly |
|||
## LNbits as an account system |
|||
LNbits is packaged with tools to help manage funds, such as a table of transactions, line chart of spending, export to csv + more to come.. |
|||
|
|||
|
|||
 |
|||
|
|||
Each wallet also comes with its own API keys, to help partition the exposure of your funding source. |
|||
|
|||
(LNbits M5StackSats available here https://github.com/arcbtc/M5StackSats) |
|||
|
|||
 |
|||
|
|||
## LNbits as an LNURL-withdraw fallback |
|||
LNURL has a fallback scheme, so if scanned by a regular QR code reader it can default to a URL. LNbits exploits this to generate an instant wallet using the LNURL-withdraw. |
|||
|
|||
 |
|||
https://github.com/btcontract/lnurl-rfc/blob/master/spec.md |
|||
|
|||
Adding **/lnurl?lightning="LNURL-WITHDRAW"** will trigger a withdraw that builds an LNbits wallet. |
|||
Example use would be an ATM, which utilises LNURL, if the user scans the QR with a regular QR code scanner app, they will stilll be able to access the funds. |
|||
|
|||
 |
|||
|
|||
## LNbits as an insta-wallet |
|||
Wallets can be easily generated and given out to people at events (one click multi-wallet generation to be added soon). |
|||
"Go to this website", has a lot less friction than "Download this app". |
|||
|
|||
 |
|||
|
|||
# Running LNbits locally |
|||
Download this repo |
|||
|
|||
LNbits uses [Flask](http://flask.pocoo.org/). |
|||
Feel free to contribute to the project. |
|||
|
|||
Application dependencies |
|||
------------------------ |
|||
The application uses [Pipenv][pipenv] to manage Python packages. |
|||
While in development, you will need to install all dependencies: |
|||
|
|||
$ pipenv shell |
|||
$ pipenv install --dev |
|||
|
|||
You will need to set the variables in .env.example, and rename the file to .env |
|||
|
|||
 |
|||
|
|||
Running the server |
|||
------------------ |
|||
|
|||
$ flask run |
|||
|
|||
There is an environment variable called `FLASK_ENV` that has to be set to `development` |
|||
if you want to run Flask in debug mode with autoreload |
|||
|
|||
[pipenv]: https://docs.pipenv.org/#install-pipenv-today |
|||
|
|||
# Tip me |
|||
If you like this project and might even use or extend it, why not send some tip love! |
|||
https://paywall.link/to/f4e4e |
@ -1,73 +0,0 @@ |
|||
|
|||
 |
|||
# LNbits |
|||
Simple free and open-source Python lightning-network wallet/accounts system. Use https://lnbits.com, or run your own LNbits server! |
|||
|
|||
LNbits is a very simple server that sits on top of a funding source, and can be used as: |
|||
* Accounts system to mitigate the risk of exposing applications to your full balance, via unique API keys for each wallet! |
|||
* Fallback wallet for the LNURL scheme |
|||
* Instant wallet for LN demonstrations |
|||
|
|||
The wallet can run on top of any lightning-network funding source such as LND, lntxbot, paywall, opennode, etc. This first BETA release runs on top of lntxbot, other releases coming soon, although if you are impatient, it could be ported to other funding sources relatively easily. Contributors very welcome :) |
|||
|
|||
LNbits is still in BETA. Please report any vulnerabilities responsibly |
|||
## LNbits as an account system |
|||
LNbits is packaged with tools to help manage funds, such as a table of transactions, line chart of spending, export to csv + more to come.. |
|||
|
|||
|
|||
 |
|||
|
|||
Each wallet also comes with its own API keys, to help partition the exposure of your funding source. |
|||
|
|||
(LNbits M5StackSats available here https://github.com/arcbtc/M5StackSats) |
|||
|
|||
 |
|||
|
|||
## LNbits as an LNURL-withdraw fallback |
|||
LNURL has a fallback scheme, so if scanned by a regular QR code reader it can default to a URL. LNbits exploits this to generate an instant wallet using the LNURL-withdraw. |
|||
|
|||
 |
|||
https://github.com/btcontract/lnurl-rfc/blob/master/spec.md |
|||
|
|||
Adding **/lnurl?lightning="LNURL-WITHDRAW"** will trigger a withdraw that builds an LNbits wallet. |
|||
Example use would be an ATM, which utilises LNURL, if the user scans the QR with a regular QR code scanner app, they will stilll be able to access the funds. |
|||
|
|||
 |
|||
|
|||
## LNbits as an insta-wallet |
|||
Wallets can be easily generated and given out to people at events (one click multi-wallet generation to be added soon). |
|||
"Go to this website", has a lot less friction than "Download this app". |
|||
|
|||
 |
|||
|
|||
# Running LNbits locally |
|||
Download this repo |
|||
|
|||
LNbits uses [Flask](http://flask.pocoo.org/). |
|||
Feel free to contribute to the project. |
|||
|
|||
Application dependencies |
|||
------------------------ |
|||
The application uses [Pipenv][pipenv] to manage Python packages. |
|||
While in development, you will need to install all dependencies: |
|||
|
|||
$ pipenv shell |
|||
$ pipenv install --dev |
|||
|
|||
You will need to set the variables in .env.example, and rename the file to .env |
|||
|
|||
 |
|||
|
|||
Running the server |
|||
------------------ |
|||
|
|||
$ flask run |
|||
|
|||
There is an environment variable called `FLASK_ENV` that has to be set to `development` |
|||
if you want to run Flask in debug mode with autoreload |
|||
|
|||
[pipenv]: https://docs.pipenv.org/#install-pipenv-today |
|||
|
|||
# Tip me |
|||
If you like this project and might even use or extend it, why not send some tip love! |
|||
https://paywall.link/to/f4e4e |
@ -0,0 +1,8 @@ |
|||
from flask import Blueprint |
|||
|
|||
|
|||
withdraw_ext = Blueprint("withdraw", __name__, static_folder="static", template_folder="templates") |
|||
|
|||
|
|||
from .views_api import * # noqa |
|||
from .views import * # noqa |
@ -1,233 +0,0 @@ |
|||
<!-- @format --> |
|||
|
|||
{% extends "base.html" %} {% block messages %} |
|||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"> |
|||
<i class="fa fa-bell-o"></i> |
|||
<span class="label label-danger">!</span> |
|||
</a> |
|||
<ul class="dropdown-menu"> |
|||
<li class="header"><b>Instant wallet, bookmark to save</b></li> |
|||
<li></li> |
|||
</ul> |
|||
{% endblock %} {% block menuitems %} |
|||
<li class="treeview"> |
|||
<a href="#"> |
|||
<i class="fa fa-bitcoin"></i> <span>Wallets</span> |
|||
<i class="fa fa-angle-left pull-right"></i> |
|||
</a> |
|||
<ul class="treeview-menu"> |
|||
{% for w in user_wallets %} |
|||
<li> |
|||
<a href="wallet?wal={{ w.id }}&usr={{ w.user }}" |
|||
><i class="fa fa-bolt"></i> {{ w.name }}</a |
|||
> |
|||
</li> |
|||
{% endfor %} |
|||
<li><a onclick="sidebarmake()">Add a wallet +</a></li> |
|||
<div id="sidebarmake"></div> |
|||
</ul> |
|||
</li> |
|||
|
|||
<li class="active treeview"> |
|||
<a href="#"> |
|||
<i class="fa fa-th"></i> <span>Extensions</span> |
|||
</a> |
|||
<ul class="treeview-menu"> |
|||
|
|||
|
|||
{% if user_ext[0][1] %} |
|||
<li> |
|||
<a href="lnevents?usr={{ user_ext[0][0]}}" |
|||
><i class="fa fa-bolt"></i> LNEvents</a> |
|||
</li> |
|||
{% endif %} |
|||
|
|||
{% if user_ext[0][2] %} |
|||
<li> |
|||
<a href="lnjoust?usr={{ user_ext[0][0]}}" |
|||
><i class="fa fa-bolt"></i> LNJoust</a> |
|||
</li> |
|||
{% endif %} |
|||
|
|||
{% if user_ext[0][3] %} |
|||
<li> |
|||
<a href="faucet?usr={{ user_ext[0][0]}}" |
|||
><i class="fa fa-bolt"></i> Faucet</a> |
|||
</li> |
|||
{% endif %} |
|||
|
|||
|
|||
</ul> |
|||
</li> |
|||
|
|||
{% endblock %} {% block body %} |
|||
<!-- Right side column. Contains the navbar and content of the page --> |
|||
<div class="content-wrapper"> |
|||
<!-- Content Header (Page header) --> |
|||
<section class="content-header"> |
|||
<h1> |
|||
Wallet |
|||
<small |
|||
>Control panel |
|||
<div id="wonga"></div |
|||
></small> |
|||
</h1> |
|||
<ol class="breadcrumb"> |
|||
<li> |
|||
<a href="#"><i class="fa fa-dashboard"></i> Home</a> |
|||
</li> |
|||
<li class="active">Extensions</li> |
|||
|
|||
</ol> |
|||
<br /><br /> |
|||
<div class="alert alert-danger alert-dismissable"> |
|||
<h4> |
|||
Bookmark to save your wallet. Wallet is in BETA, use with caution. |
|||
</h4> |
|||
</div> |
|||
</section> |
|||
|
|||
<!-- Main content --> |
|||
<section class="content"> |
|||
<!-- Small boxes (Stat box) --> |
|||
<div class="row"> |
|||
|
|||
{% if not user_ext[0][2] %} |
|||
<div class="col-lg-3 col-xs-6"> |
|||
<!-- small box --> |
|||
<div class="small-box bg-green"> |
|||
<div class="inner"> |
|||
<h3> |
|||
LNJoust |
|||
</h3> |
|||
<p> |
|||
LN powered Joust gamesmaster |
|||
</p> |
|||
</div> |
|||
<div class="icon"> |
|||
<i class="ion ion-wand"></i> |
|||
</div> |
|||
|
|||
<a href="extensions?usr={{user}}&lnjoust=1" class="small-box-footer"> |
|||
Activate <i class="fa fa-arrow-circle-right"></i> |
|||
</a> |
|||
</div> |
|||
</div><!-- ./col --> |
|||
|
|||
{% else %} |
|||
<div class="col-lg-3 col-xs-6"> |
|||
<!-- small box --> |
|||
<div class="small-box bg-green"> |
|||
<div class="inner"> |
|||
<h3> |
|||
LNJoust |
|||
</h3> |
|||
<p> |
|||
LN powered Joust gamesmaster |
|||
</p> |
|||
</div> |
|||
<div class="icon"> |
|||
<i class="ion ion-wand"></i> |
|||
</div> |
|||
<a href="extensions?usr={{user}}&lnjoust=0" class="small-box-footer"> |
|||
Deactivate <i class="fa fa-arrow-circle-right"></i> |
|||
</a> |
|||
</div> |
|||
</div><!-- ./col --> |
|||
{% endif %} |
|||
{% if not user_ext[0][1] %} |
|||
<div class="col-lg-3 col-xs-6"> |
|||
<!-- small box --> |
|||
<div class="small-box bg-yellow"> |
|||
<div class="inner"> |
|||
<h3> |
|||
LNEvents |
|||
</h3> |
|||
<p> |
|||
Lightning powered tickets |
|||
</p> |
|||
</div> |
|||
<div class="icon"> |
|||
<i class="ion ion-calendar"></i> |
|||
</div> |
|||
<a href="extensions?usr={{user}}&lnevents=1" class="small-box-footer"> |
|||
Activate <i class="fa fa-arrow-circle-right"></i> |
|||
</a> |
|||
</div> |
|||
</div><!-- ./col --> |
|||
|
|||
{% else %} |
|||
<div class="col-lg-3 col-xs-6"> |
|||
<!-- small box --> |
|||
<div class="small-box bg-yellow"> |
|||
<div class="inner"> |
|||
<h3> |
|||
LNEvents |
|||
</h3> |
|||
<p> |
|||
Lightning powered tickets |
|||
</p> |
|||
</div> |
|||
<div class="icon"> |
|||
<i class="ion ion-calendar"></i> |
|||
</div> |
|||
<a href="extensions?usr={{user}}&lnevents=0" class="small-box-footer"> |
|||
Deactivate <i class="fa fa-arrow-circle-right"></i> |
|||
</a> |
|||
</div> |
|||
</div><!-- ./col --> |
|||
{% endif %} |
|||
{% if not user_ext[0][3] %} |
|||
<div class="col-lg-3 col-xs-6"> |
|||
<!-- small box --> |
|||
<div class="small-box bg-red"> |
|||
<div class="inner"> |
|||
<h3> |
|||
Faucet |
|||
</h3> |
|||
<p> |
|||
Make LNURL faucets |
|||
</p> |
|||
</div> |
|||
<div class="icon"> |
|||
<i class="ion ion-beer"></i> |
|||
</div> |
|||
<a href="extensions?usr={{user}}&faucet=1" class="small-box-footer"> |
|||
Activate <i class="fa fa-arrow-circle-right"></i> |
|||
</a> |
|||
</div> |
|||
</div><!-- ./col --> |
|||
{% else %} |
|||
<div class="col-lg-3 col-xs-6"> |
|||
<!-- small box --> |
|||
<div class="small-box bg-red"> |
|||
<div class="inner"> |
|||
<h3> |
|||
Faucet |
|||
</h3> |
|||
<p> |
|||
Make LNURL faucets |
|||
</p> |
|||
</div> |
|||
<div class="icon"> |
|||
<i class="ion ion-beer"></i> |
|||
</div> |
|||
<a href="extensions?usr={{user}}&faucet=0" class="small-box-footer"> |
|||
Deactivate <i class="fa fa-arrow-circle-right"></i> |
|||
</a> |
|||
</div> |
|||
</div><!-- ./col --> |
|||
{% endif %} |
|||
</div> |
|||
|
|||
<!-- /.content --> |
|||
</section> |
|||
|
|||
<script> |
|||
window.user = {{ user | megajson | safe }} |
|||
window.user_wallets = {{ user_wallets | megajson | safe }} |
|||
window.user_ext = {{ user_ext | megajson | safe }} |
|||
|
|||
</script> |
|||
</div> |
|||
{% endblock %} |
@ -0,0 +1,160 @@ |
|||
import uuid |
|||
|
|||
from flask import jsonify, render_template, request, redirect, url_for |
|||
from lnurl import encode as lnurl_encode |
|||
from datetime import datetime |
|||
|
|||
from lnbits.db import open_db, open_ext_db |
|||
from lnbits.extensions.withdraw import withdraw_ext |
|||
|
|||
|
|||
@withdraw_ext.route("/") |
|||
def index(): |
|||
"""Main withdraw link page.""" |
|||
|
|||
usr = request.args.get("usr") |
|||
|
|||
if usr: |
|||
if not len(usr) > 20: |
|||
return redirect(url_for("home")) |
|||
|
|||
# Get all the data |
|||
with open_db() as db: |
|||
user_wallets = db.fetchall("SELECT * FROM wallets WHERE user = ?", (usr,)) |
|||
|
|||
with open_ext_db() as ext_db: |
|||
user_ext = ext_db.fetchall("SELECT * FROM overview WHERE user = ?", (usr,)) |
|||
|
|||
with open_ext_db("withdraw") as withdraw_ext_db: |
|||
user_fau = withdraw_ext_db.fetchall("SELECT * FROM withdraws WHERE usr = ?", (usr,)) |
|||
|
|||
# If del is selected by user from withdraw page, the withdraw link is to be deleted |
|||
faudel = request.args.get("del") |
|||
if faudel: |
|||
withdraw_ext_db.execute("DELETE FROM withdraws WHERE uni = ?", (faudel,)) |
|||
user_fau = withdraw_ext_db.fetchall("SELECT * FROM withdraws WHERE usr = ?", (usr,)) |
|||
|
|||
return render_template( |
|||
"withdraw/index.html", user_wallets=user_wallets, user=usr, user_ext=user_ext, user_fau=user_fau |
|||
) |
|||
|
|||
|
|||
@withdraw_ext.route("/create", methods=["GET", "POST"]) |
|||
def create(): |
|||
""".""" |
|||
|
|||
data = request.json |
|||
amt = data["amt"] |
|||
tit = data["tit"] |
|||
wal = data["wal"] |
|||
minamt = data["minamt"] |
|||
maxamt = data["maxamt"] |
|||
tme = data["tme"] |
|||
uniq = data["uniq"] |
|||
usr = data["usr"] |
|||
wall = wal.split("-") |
|||
|
|||
# Form validation |
|||
if ( |
|||
int(amt) < 0 |
|||
or not tit.replace(" ", "").isalnum() |
|||
or wal == "" |
|||
or int(minamt) < 0 |
|||
or int(maxamt) < 0 |
|||
or int(minamt) > int(maxamt) |
|||
or int(tme) < 0 |
|||
): |
|||
return jsonify({"ERROR": "FORM ERROR"}), 401 |
|||
|
|||
# If id that means its a link being edited, delet the record first |
|||
if "id" in data: |
|||
unid = data["id"].split("-") |
|||
uni = unid[1] |
|||
with open_ext_db("withdraw") as withdraw_ext_db: |
|||
withdraw_ext_db.execute("DELETE FROM withdraws WHERE uni = ?", (unid[1],)) |
|||
else: |
|||
uni = uuid.uuid4().hex |
|||
|
|||
# Randomiser for random QR option |
|||
rand = "" |
|||
if uniq > 0: |
|||
for x in range(0, int(amt)): |
|||
rand += uuid.uuid4().hex[0:5] + "," |
|||
else: |
|||
rand = uuid.uuid4().hex[0:5] + "," |
|||
|
|||
with open_db() as dbb: |
|||
user_wallets = dbb.fetchall("SELECT * FROM wallets WHERE user = ? AND id = ?", (usr, wall[1],)) |
|||
if not user_wallets: |
|||
return jsonify({"ERROR": "NO WALLET USER"}), 401 |
|||
|
|||
# Get time |
|||
dt = datetime.now() |
|||
seconds = dt.timestamp() |
|||
|
|||
# Add to DB |
|||
with open_ext_db("withdraw") as db: |
|||
db.execute( |
|||
"INSERT OR IGNORE INTO withdraws (usr, wal, walnme, adm, uni, tit, maxamt, minamt, spent, inc, tme, uniq, withdrawals, tmestmp, rand) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", |
|||
( |
|||
usr, |
|||
wall[1], |
|||
user_wallets[0][1], |
|||
user_wallets[0][3], |
|||
uni, |
|||
tit, |
|||
maxamt, |
|||
minamt, |
|||
0, |
|||
amt, |
|||
tme, |
|||
uniq, |
|||
0, |
|||
seconds, |
|||
rand, |
|||
), |
|||
) |
|||
|
|||
# Get updated records |
|||
with open_ext_db() as ext_db: |
|||
user_ext = ext_db.fetchall("SELECT * FROM overview WHERE user = ?", (usr,)) |
|||
if not user_ext: |
|||
return jsonify({"ERROR": "NO WALLET USER"}), 401 |
|||
|
|||
with open_ext_db("withdraw") as withdraw_ext_db: |
|||
user_fau = withdraw_ext_db.fetchall("SELECT * FROM withdraws WHERE usr = ?", (usr,)) |
|||
if not user_fau: |
|||
return jsonify({"ERROR": "NO WALLET USER"}), 401 |
|||
|
|||
return render_template( |
|||
"withdraw/index.html", user_wallets=user_wallets, user=usr, user_ext=user_ext, user_fau=user_fau |
|||
) |
|||
|
|||
|
|||
@withdraw_ext.route("/display", methods=["GET", "POST"]) |
|||
def display(): |
|||
"""Simple shareable link.""" |
|||
fauid = request.args.get("id") |
|||
|
|||
with open_ext_db("withdraw") as withdraw_ext_db: |
|||
user_fau = withdraw_ext_db.fetchall("SELECT * FROM withdraws WHERE uni = ?", (fauid,)) |
|||
|
|||
return render_template("withdraw/display.html", user_fau=user_fau,) |
|||
|
|||
|
|||
@withdraw_ext.route("/print/<urlstr>/", methods=["GET", "POST"]) |
|||
def print_qr(urlstr): |
|||
"""Simple printable page of links.""" |
|||
fauid = request.args.get("id") |
|||
|
|||
with open_ext_db("withdraw") as withdraw_ext_db: |
|||
user_fau = withdraw_ext_db.fetchall("SELECT * FROM withdraws WHERE uni = ?", (fauid,)) |
|||
randar = user_fau[0][15].split(",") |
|||
randar = randar[:-1] |
|||
lnurlar = [] |
|||
|
|||
for d in range(len(randar)): |
|||
url = url_for("withdraw.api_lnurlfetch", _external=True, urlstr=urlstr, parstr=fauid, rand=randar[d]) |
|||
lnurlar.append(lnurl_encode(url.replace("http", "https"))) |
|||
|
|||
return render_template("withdraw/print.html", lnurlar=lnurlar, user_fau=user_fau[0],) |
@ -0,0 +1,116 @@ |
|||
import uuid |
|||
import json |
|||
import requests |
|||
|
|||
from flask import jsonify, request, url_for |
|||
from lnurl import LnurlWithdrawResponse, encode as lnurl_encode |
|||
from datetime import datetime |
|||
|
|||
from lnbits.db import open_ext_db |
|||
from lnbits.extensions.withdraw import withdraw_ext |
|||
|
|||
|
|||
@withdraw_ext.route("/api/v1/lnurlencode/<urlstr>/<parstr>", methods=["GET"]) |
|||
def api_lnurlencode(urlstr, parstr): |
|||
"""Returns encoded LNURL if web url and parameter gieven.""" |
|||
|
|||
if not urlstr: |
|||
return jsonify({"status": "FALSE"}), 200 |
|||
|
|||
with open_ext_db("withdraw") as withdraw_ext_db: |
|||
user_fau = withdraw_ext_db.fetchall("SELECT * FROM withdraws WHERE uni = ?", (parstr,)) |
|||
randar = user_fau[0][15].split(",") |
|||
# randar = randar[:-1] |
|||
# If "Unique links" selected get correct rand, if not there is only one rand |
|||
if user_fau[0][12] > 0: |
|||
rand = randar[user_fau[0][10] - 2] |
|||
else: |
|||
rand = randar[0] |
|||
|
|||
url = url_for("withdraw.api_lnurlfetch", _external=True, urlstr=urlstr, parstr=parstr, rand=rand) |
|||
|
|||
return jsonify({"status": "TRUE", "lnurl": lnurl_encode(url.replace("http", "https"))}), 200 |
|||
|
|||
|
|||
@withdraw_ext.route("/api/v1/lnurlfetch/<urlstr>/<parstr>/<rand>", methods=["GET"]) |
|||
def api_lnurlfetch(parstr, urlstr, rand): |
|||
"""Returns LNURL json.""" |
|||
|
|||
if not parstr: |
|||
return jsonify({"status": "FALSE", "ERROR": "NO WALL ID"}), 200 |
|||
|
|||
if not urlstr: |
|||
|
|||
return jsonify({"status": "FALSE", "ERROR": "NO URL"}), 200 |
|||
|
|||
with open_ext_db("withdraw") as withdraw_ext_db: |
|||
user_fau = withdraw_ext_db.fetchall("SELECT * FROM withdraws WHERE uni = ?", (parstr,)) |
|||
k1str = uuid.uuid4().hex |
|||
withdraw_ext_db.execute("UPDATE withdraws SET withdrawals = ? WHERE uni = ?", (k1str, parstr,)) |
|||
|
|||
res = LnurlWithdrawResponse( |
|||
callback=url_for("withdraw.api_lnurlwithdraw", _external=True, rand=rand).replace("http", "https"), |
|||
k1=k1str, |
|||
min_withdrawable=user_fau[0][8] * 1000, |
|||
max_withdrawable=user_fau[0][7] * 1000, |
|||
default_description="LNbits LNURL withdraw", |
|||
) |
|||
|
|||
return res.json(), 200 |
|||
|
|||
|
|||
@withdraw_ext.route("/api/v1/lnurlwithdraw/<rand>/", methods=["GET"]) |
|||
def api_lnurlwithdraw(rand): |
|||
"""Pays invoice if passed k1 invoice and rand.""" |
|||
|
|||
k1 = request.args.get("k1") |
|||
pr = request.args.get("pr") |
|||
|
|||
if not k1: |
|||
return jsonify({"status": "FALSE", "ERROR": "NO k1"}), 200 |
|||
|
|||
if not pr: |
|||
return jsonify({"status": "FALSE", "ERROR": "NO PR"}), 200 |
|||
|
|||
with open_ext_db("withdraw") as withdraw_ext_db: |
|||
user_fau = withdraw_ext_db.fetchall("SELECT * FROM withdraws WHERE withdrawals = ?", (k1,)) |
|||
|
|||
if not user_fau: |
|||
return jsonify({"status": "ERROR", "reason": "NO AUTH"}), 400 |
|||
|
|||
if user_fau[0][10] < 1: |
|||
return jsonify({"status": "ERROR", "reason": "withdraw SPENT"}), 400 |
|||
|
|||
# Check withdraw time |
|||
dt = datetime.now() |
|||
seconds = dt.timestamp() |
|||
secspast = seconds - user_fau[0][14] |
|||
|
|||
if secspast < user_fau[0][11]: |
|||
return jsonify({"status": "ERROR", "reason": "WAIT " + str(int(user_fau[0][11] - secspast)) + "s"}), 400 |
|||
|
|||
randar = user_fau[0][15].split(",") |
|||
if rand not in randar: |
|||
return jsonify({"status": "ERROR", "reason": "BAD AUTH"}), 400 |
|||
if len(randar) > 2: |
|||
randar.remove(rand) |
|||
randstr = ",".join(randar) |
|||
|
|||
# Update time and increments |
|||
upinc = int(user_fau[0][10]) - 1 |
|||
withdraw_ext_db.execute( |
|||
"UPDATE withdraws SET inc = ?, rand = ?, tmestmp = ? WHERE withdrawals = ?", (upinc, randstr, seconds, k1,) |
|||
) |
|||
|
|||
header = {"Content-Type": "application/json", "Grpc-Metadata-macaroon": str(user_fau[0][4])} |
|||
data = {"payment_request": pr} |
|||
r = requests.post(url=url_for("api_transactions", _external=True), headers=header, data=json.dumps(data)) |
|||
r_json = r.json() |
|||
|
|||
if "ERROR" in r_json: |
|||
return jsonify({"status": "ERROR", "reason": r_json["ERROR"]}), 400 |
|||
|
|||
with open_ext_db("withdraw") as withdraw_ext_db: |
|||
user_fau = withdraw_ext_db.fetchall("SELECT * FROM withdraws WHERE withdrawals = ?", (k1,)) |
|||
|
|||
return jsonify({"status": "OK"}), 200 |
Loading…
Reference in new issue