diff --git a/.env.example b/.env.example index 46a0215..3a312ac 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,10 @@ FLASK_APP=lnbits FLASK_ENV=development +LNBITS_WITH_ONION=0 +LNBITS_DEFAULT_WALLET_NAME="LNbits wallet" +LNBITS_FEE_RESERVE=0 + LND_API_ENDPOINT=https://mylnd.io/rest/ LND_ADMIN_MACAROON=LND_ADMIN_MACAROON LND_INVOICE_MACAROON=LND_INVOICE_MACAROON @@ -15,9 +19,7 @@ OPENNODE_ADMIN_KEY=OPENNODE_ADMIN_KEY OPENNODE_INVOICE_KEY=OPENNODE_INVOICE_KEY LNPAY_API_ENDPOINT=https://lnpay.co/v1/ +LNPAY_API_KEY=LNPAY_API_KEY LNPAY_ADMIN_KEY=LNPAY_ADMIN_KEY LNPAY_INVOICE_KEY=LNPAY_INVOICE_KEY LNPAY_READ_KEY=LNPAY_READ_KEY -LNPAY_API_KEY=LNPAY_API_KEY - -FEE_RESERVE=0 diff --git a/Pipfile b/Pipfile index 4402bde..423ac93 100644 --- a/Pipfile +++ b/Pipfile @@ -12,6 +12,8 @@ lnurl = "*" flask = "*" flask-assets = "*" flask-compress = "*" +flask-limiter = "*" +flask-seasurf = "*" flask-talisman = "*" gevent = "*" greenlet = "*" diff --git a/lnbits/__init__.py b/lnbits/__init__.py index d3179cc..5ac9c04 100644 --- a/lnbits/__init__.py +++ b/lnbits/__init__.py @@ -1,18 +1,14 @@ import importlib -import json -import requests -import uuid -from flask import Flask, redirect, render_template, request, url_for +from flask import Flask from flask_assets import Environment, Bundle from flask_compress import Compress from flask_talisman import Talisman -from lnurl import Lnurl, LnurlWithdrawResponse +from os import getenv from .core import core_app -from .db import init_databases, open_db +from .db import init_databases from .helpers import ExtensionManager, megajson -from .settings import WALLET, DEFAULT_USER_WALLET_NAME app = Flask(__name__) @@ -25,6 +21,7 @@ valid_extensions = [ext for ext in ExtensionManager().extensions if ext.is_valid Compress(app) Talisman( app, + force_https=getenv("LNBITS_WITH_ONION", 0) == 0, content_security_policy={ "default-src": [ "'self'", @@ -81,69 +78,5 @@ def init(): init_databases() -# vvvvvvvvvvvvvvvvvvvvvvvvvvv -# move the rest to `core_app` -# vvvvvvvvvvvvvvvvvvvvvvvvvvv -# vvvvvvvvvvvvvvvvvvvvvvvvvvv - - -@app.route("/lnurl") -def lnurl(): - lnurl = request.args.get("lightning") - return render_template("lnurl.html", lnurl=lnurl) - - -@app.route("/lnurlwallet") -def lnurlwallet(): - lnurl = Lnurl(request.args.get("lightning")) - r = requests.get(lnurl.url) - if not r.ok: - return redirect(url_for("home")) - - data = json.loads(r.text) - if data.get("status") == "ERROR": - return redirect(url_for("home")) - - withdraw_res = LnurlWithdrawResponse(**data) - - _, pay_hash, pay_req = WALLET.create_invoice(withdraw_res.max_sats, "LNbits lnurl funding") - - r = requests.get( - withdraw_res.callback.base, - params={**withdraw_res.callback.query_params, **{"k1": withdraw_res.k1, "pr": pay_req}}, - ) - - if not r.ok: - return redirect(url_for("home")) - data = json.loads(r.text) - - for i in range(10): - r = WALLET.get_invoice_status(pay_hash).raw_response - if not r.ok: - continue - - data = r.json() - break - - with open_db() as db: - wallet_id = uuid.uuid4().hex - user_id = uuid.uuid4().hex - wallet_name = DEFAULT_USER_WALLET_NAME - adminkey = uuid.uuid4().hex - inkey = uuid.uuid4().hex - - db.execute("INSERT INTO accounts (id) VALUES (?)", (user_id,)) - db.execute( - "INSERT INTO wallets (id, name, user, adminkey, inkey) VALUES (?, ?, ?, ?, ?)", - (wallet_id, wallet_name, user_id, adminkey, inkey), - ) - db.execute( - "INSERT INTO apipayments (payhash, amount, wallet, pending, memo) VALUES (?, ?, ?, 0, ?)", - (pay_hash, withdraw_res.max_sats * 1000, wallet_id, "LNbits lnurl funding",), - ) - - return redirect(url_for("wallet", usr=user_id, wal=wallet_id)) - - if __name__ == '__main__': app.run() diff --git a/lnbits/core/__init__.py b/lnbits/core/__init__.py index 2bff1be..a2e89d8 100644 --- a/lnbits/core/__init__.py +++ b/lnbits/core/__init__.py @@ -4,5 +4,6 @@ from flask import Blueprint core_app = Blueprint("core", __name__, template_folder="templates", static_folder="static") -from .views_api import * # noqa -from .views import * # noqa +from .views.api import * # noqa +from .views.generic import * # noqa +from .views.lnurl import * # noqa diff --git a/lnbits/core/crud.py b/lnbits/core/crud.py index 51431c8..7620ed8 100644 --- a/lnbits/core/crud.py +++ b/lnbits/core/crud.py @@ -1,7 +1,7 @@ from uuid import uuid4 from lnbits.db import open_db -from lnbits.settings import DEFAULT_USER_WALLET_NAME, FEE_RESERVE +from lnbits.settings import DEFAULT_WALLET_NAME, FEE_RESERVE from typing import List, Optional from .models import User, Wallet, Payment @@ -71,7 +71,7 @@ def create_wallet(*, user_id: str, wallet_name: Optional[str]) -> Wallet: INSERT INTO wallets (id, name, user, adminkey, inkey) VALUES (?, ?, ?, ?, ?) """, - (wallet_id, wallet_name or DEFAULT_USER_WALLET_NAME, user_id, uuid4().hex, uuid4().hex), + (wallet_id, wallet_name or DEFAULT_WALLET_NAME, user_id, uuid4().hex, uuid4().hex), ) return get_wallet(wallet_id=wallet_id) diff --git a/lnbits/core/migrations/0001_initial.py b/lnbits/core/migrations/0001_initial.py new file mode 100644 index 0000000..e69de29 diff --git a/lnbits/core/migrations/__init__.py b/lnbits/core/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lnbits/core/static/js/lnurl.js b/lnbits/core/static/js/lnurl.js new file mode 100644 index 0000000..0670460 --- /dev/null +++ b/lnbits/core/static/js/lnurl.js @@ -0,0 +1,13 @@ +new Vue({ + el: '#vue', + mixins: [windowMixin], + methods: { + notify: function () { + this.$q.notify({ + timeout: 0, + message: 'Processing...', + icon: null + }); + } + } +}); diff --git a/lnbits/core/templates/core/index.html b/lnbits/core/templates/core/index.html index 434bfbc..5f1bc47 100644 --- a/lnbits/core/templates/core/index.html +++ b/lnbits/core/templates/core/index.html @@ -15,20 +15,22 @@
- - - - - Add a new wallet - - - + {% block call_to_action %} + + + + + Add a new wallet + + + + {% endblock %} diff --git a/lnbits/core/templates/core/lnurl.html b/lnbits/core/templates/core/lnurl.html new file mode 100644 index 0000000..cad7ccb --- /dev/null +++ b/lnbits/core/templates/core/lnurl.html @@ -0,0 +1,22 @@ +{% extends "core/index.html" %} + + +{% block scripts %} + {% assets filters='rjsmin', output='__bundle__/core/lnurl.js', + 'core/js/lnurl.js' %} + + {% endassets %} +{% endblock %} + +{% block call_to_action %} + + + + Press to claim bitcoin + + + +{% endblock %} diff --git a/lnbits/core/views/__init__.py b/lnbits/core/views/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lnbits/core/views_api.py b/lnbits/core/views/api.py similarity index 99% rename from lnbits/core/views_api.py rename to lnbits/core/views/api.py index eb0e044..e801024 100644 --- a/lnbits/core/views_api.py +++ b/lnbits/core/views/api.py @@ -6,7 +6,7 @@ from lnbits.decorators import api_check_wallet_macaroon, api_validate_post_reque from lnbits.helpers import Status from lnbits.settings import FEE_RESERVE, WALLET -from .crud import create_payment +from ..crud import create_payment @core_app.route("/api/v1/payments", methods=["GET"]) diff --git a/lnbits/core/views.py b/lnbits/core/views/generic.py similarity index 99% rename from lnbits/core/views.py rename to lnbits/core/views/generic.py index a742772..1c918a4 100644 --- a/lnbits/core/views.py +++ b/lnbits/core/views/generic.py @@ -5,7 +5,7 @@ from lnbits.core import core_app from lnbits.decorators import check_user_exists, validate_uuids from lnbits.helpers import Status -from .crud import ( +from ..crud import ( create_account, get_user, update_user_extension, diff --git a/lnbits/core/views/lnurl.py b/lnbits/core/views/lnurl.py new file mode 100644 index 0000000..02f480f --- /dev/null +++ b/lnbits/core/views/lnurl.py @@ -0,0 +1,55 @@ +import json +import requests + +from flask import redirect, render_template, request, url_for +from lnurl import LnurlWithdrawResponse, handle as handle_lnurl +from lnurl.exceptions import LnurlException +from time import sleep + +from lnbits.core import core_app +from lnbits.settings import WALLET + +from ..crud import create_account, get_user, create_wallet, create_payment + + +@core_app.route("/lnurl") +def lnurl(): + lnurl = request.args.get("lightning") + return render_template("core/lnurl.html", lnurl=lnurl) + + +@core_app.route("/lnurlwallet") +def lnurlwallet(): + try: + withdraw_res = handle_lnurl(request.args.get("lightning"), response_class=LnurlWithdrawResponse) + except LnurlException: + return redirect(url_for("core.home")) + + _, payhash, payment_request = WALLET.create_invoice(withdraw_res.max_sats, "LNbits LNURL funding") + + r = requests.get( + withdraw_res.callback.base, + params={**withdraw_res.callback.query_params, **{"k1": withdraw_res.k1, "pr": payment_request}}, + ) + + if not r.ok: + return redirect(url_for("core.home")) # TODO: custom error + data = json.loads(r.text) + + for i in range(10): + r = WALLET.get_invoice_status(payhash).raw_response + sleep(i) + if not r.ok: + continue + break + + user = get_user(create_account().id) + wallet = create_wallet(user_id=user.id) + create_payment( # TODO: not pending? + wallet_id=wallet.id, + payhash=payhash, + amount=withdraw_res.max_sats * 1000, + memo="LNbits lnurl funding", + ) + + return redirect(url_for("core.wallet", usr=user.id, wal=wallet.id)) diff --git a/lnbits/settings.py b/lnbits/settings.py index 8c21e1a..34ba3eb 100644 --- a/lnbits/settings.py +++ b/lnbits/settings.py @@ -11,5 +11,5 @@ WALLET = OpenNodeWallet(endpoint=os.getenv("OPENNODE_API_ENDPOINT"),admin_key=os LNBITS_PATH = os.path.dirname(os.path.realpath(__file__)) LNBITS_DATA_FOLDER = os.getenv("LNBITS_DATA_FOLDER", os.path.join(LNBITS_PATH, "data")) -DEFAULT_USER_WALLET_NAME = os.getenv("DEFAULT_USER_WALLET_NAME", "LNbits wallet") -FEE_RESERVE = float(os.getenv("FEE_RESERVE", 0)) +DEFAULT_WALLET_NAME = os.getenv("LNBITS_DEFAULT_WALLET_NAME", "LNbits wallet") +FEE_RESERVE = float(os.getenv("LNBITS_FEE_RESERVE", 0)) diff --git a/lnbits/templates/base.html b/lnbits/templates/base.html index bec735f..7aca622 100644 --- a/lnbits/templates/base.html +++ b/lnbits/templates/base.html @@ -31,8 +31,10 @@ - - + {% block drawer %} + + + {% endblock %} diff --git a/lnbits/templates/lnurl.html b/lnbits/templates/lnurl.html deleted file mode 100644 index aa97ff0..0000000 --- a/lnbits/templates/lnurl.html +++ /dev/null @@ -1,15 +0,0 @@ - - -{% extends "index.html" %} {% block call_to_action %} - - -
-
-{% endblock %} diff --git a/requirements.txt b/requirements.txt index dfbecba..814f067 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,6 +5,8 @@ chardet==3.0.4 click==7.0 flask-assets==2.0 flask-compress==1.4.0 +flask-limiter==1.2.1 +flask-seasurf==0.2.2 flask-talisman==0.7.0 flask==1.1.1 gevent==1.4.0 @@ -13,6 +15,7 @@ gunicorn==20.0.4 idna==2.9 itsdangerous==1.1.0 jinja2==2.11.1 +limits==1.5.1 lnurl==0.3.3 markupsafe==1.1.1 pydantic==1.4 @@ -23,4 +26,3 @@ typing-extensions==3.7.4.1 ; python_version < '3.8' urllib3==1.25.8 webassets==2.0 werkzeug==1.0.0 -python-dotenv==0.12.0