From 0d33bc29332871c8a80af9a59b507907928cd7c5 Mon Sep 17 00:00:00 2001 From: Eneko Illarramendi Date: Mon, 10 Feb 2020 13:29:20 +0100 Subject: [PATCH] refactor: remove databases from code an use schemas instead All databases are now saved together in the same LNBITS_DATA_FOLDER. Extensions have to define a schema.yml file for creating the necessary database. --- .gitignore | 3 +- lnbits/__init__.py | 92 +++++++++--------- lnbits/data/schema.sql | 48 +++++---- lnbits/db.py | 31 ++++-- lnbits/extensions/overview.sqlite3 | Bin 12288 -> 0 bytes lnbits/extensions/withdraw/crud.py | 0 lnbits/extensions/withdraw/database.sqlite3 | Bin 16384 -> 0 bytes lnbits/extensions/withdraw/schema.sql | 18 ++++ .../withdraw/templates/withdraw/index.html | 4 +- lnbits/extensions/withdraw/views.py | 27 ++--- lnbits/extensions/withdraw/views_api.py | 9 +- lnbits/helpers.py | 9 +- lnbits/settings.py | 4 +- lnbits/templates/extensions.html | 33 +++---- 14 files changed, 153 insertions(+), 125 deletions(-) delete mode 100644 lnbits/extensions/overview.sqlite3 delete mode 100644 lnbits/extensions/withdraw/crud.py delete mode 100644 lnbits/extensions/withdraw/database.sqlite3 create mode 100644 lnbits/extensions/withdraw/schema.sql diff --git a/.gitignore b/.gitignore index 0748bfb6e..a0b28d678 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,5 @@ Pipfile.lock .env venv -database.sqlite3 -database.sqlite3* +*.sqlite3 .pyre* diff --git a/lnbits/__init__.py b/lnbits/__init__.py index d2cc75321..e2cfc2192 100644 --- a/lnbits/__init__.py +++ b/lnbits/__init__.py @@ -1,5 +1,4 @@ import json -import os import requests import uuid @@ -9,26 +8,29 @@ from lnurl import Lnurl, LnurlWithdrawResponse from . import bolt11 from .core import core_app -from .db import open_db, open_ext_db +from .db import init_databases, open_db from .extensions.withdraw import withdraw_ext from .helpers import megajson -from .settings import LNBITS_PATH, WALLET, DEFAULT_USER_WALLET_NAME, FEE_RESERVE +from .settings import WALLET, DEFAULT_USER_WALLET_NAME, FEE_RESERVE app = Flask(__name__) -Talisman(app, content_security_policy={ - "default-src": [ - "'self'", - "'unsafe-eval'", - "'unsafe-inline'", - "cdnjs.cloudflare.com", - "code.ionicframework.com", - "code.jquery.com", - "fonts.googleapis.com", - "fonts.gstatic.com", - "maxcdn.bootstrapcdn.com", - ] -}) +Talisman( + app, + content_security_policy={ + "default-src": [ + "'self'", + "'unsafe-eval'", + "'unsafe-inline'", + "cdnjs.cloudflare.com", + "code.ionicframework.com", + "code.jquery.com", + "fonts.googleapis.com", + "fonts.gstatic.com", + "maxcdn.bootstrapcdn.com", + ] + }, +) # filters app.jinja_env.filters["megajson"] = megajson @@ -40,10 +42,7 @@ app.register_blueprint(withdraw_ext, url_prefix="/withdraw") @app.before_first_request def init(): - with open_db() as db: - with open(os.path.join(LNBITS_PATH, "data", "schema.sql")) as schemafile: - for stmt in schemafile.read().split(";\n\n"): - db.execute(stmt, []) + init_databases() @app.route("/deletewallet") @@ -447,38 +446,35 @@ def api_checkpending(): @app.route("/extensions") def extensions(): usr = request.args.get("usr") - lnevents = request.args.get("lnevents") - lnjoust = request.args.get("lnjoust") - withdraw = request.args.get("withdraw") - if usr: - if not len(usr) > 20: - return redirect(url_for("home")) + enable = request.args.get("enable") + disable = request.args.get("disable") + ext = None + + if usr and not len(usr) > 20: + return redirect(url_for("home")) + + if enable and disable: + # TODO: show some kind of error + return redirect(url_for("extensions")) 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,)) - if not user_ext: - ext_db.execute( - """ - INSERT OR IGNORE INTO overview (user) VALUES (?) - """, - (usr,), - ) - return redirect(url_for("extensions", usr=usr)) + if enable: + ext, value = enable, 1 + if disable: + ext, value = disable, 0 - if lnevents: - if int(lnevents) != user_ext[0][1] and int(lnevents) < 2: - ext_db.execute("UPDATE overview SET lnevents = ? WHERE user = ?", (int(lnevents), usr,)) - user_ext = ext_db.fetchall("SELECT * FROM overview WHERE user = ?", (usr,)) - if lnjoust: - if int(lnjoust) != user_ext[0][2] and int(lnjoust) < 2: - ext_db.execute("UPDATE overview SET lnjoust = ? WHERE user = ?", (int(lnjoust), usr,)) - user_ext = ext_db.fetchall("SELECT * FROM overview WHERE user = ?", (usr,)) - if withdraw: - if int(withdraw) != user_ext[0][3] and int(withdraw) < 2: - ext_db.execute("UPDATE overview SET withdraw = ? WHERE user = ?", (int(withdraw), usr,)) - user_ext = ext_db.fetchall("SELECT * FROM overview WHERE user = ?", (usr,)) + if ext: + db.execute( + """ + INSERT OR REPLACE INTO extensions (user, extension, active) + VALUES (?, ?, ?) + """, + (usr, ext, value), + ) + + user_ext = db.fetchall("SELECT extension FROM extensions WHERE user = ? AND active = 1", (usr,)) + user_ext = [v[0] for v in user_ext] return render_template("extensions.html", user_wallets=user_wallets, user=usr, user_ext=user_ext) diff --git a/lnbits/data/schema.sql b/lnbits/data/schema.sql index 2c9991d75..37fceb4d6 100644 --- a/lnbits/data/schema.sql +++ b/lnbits/data/schema.sql @@ -1,39 +1,47 @@ CREATE TABLE IF NOT EXISTS accounts ( - id text PRIMARY KEY, - email text, - pass text + id TEXT PRIMARY KEY, + email TEXT, + pass TEXT +); + +CREATE TABLE IF NOT EXISTS extensions ( + user TEXT NOT NULL, + extension TEXT NOT NULL, + active BOOLEAN DEFAULT 0, + + UNIQUE (user, extension) ); CREATE TABLE IF NOT EXISTS wallets ( - id text PRIMARY KEY, - name text NOT NULL, - user text NOT NULL, - adminkey text NOT NULL, - inkey text + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + user TEXT NOT NULL, + adminkey TEXT NOT NULL, + inkey TEXT ); CREATE TABLE IF NOT EXISTS apipayments ( - payhash text NOT NULL, - amount integer NOT NULL, - fee integer NOT NULL DEFAULT 0, - wallet text NOT NULL, - pending boolean NOT NULL, - memo text, - time timestamp NOT NULL DEFAULT (strftime('%s', 'now')), + payhash TEXT NOT NULL, + amount INTEGER NOT NULL, + fee INTEGER NOT NULL DEFAULT 0, + wallet TEXT NOT NULL, + pending BOOLEAN NOT NULL, + memo TEXT, + time TIMESTAMP NOT NULL DEFAULT (strftime('%s', 'now')), UNIQUE (wallet, payhash) ); CREATE VIEW IF NOT EXISTS balances AS - SELECT wallet, coalesce(sum(s), 0) AS balance FROM ( - SELECT wallet, sum(amount) AS s -- incoming + SELECT wallet, COALESCE(SUM(s), 0) AS balance FROM ( + SELECT wallet, SUM(amount) AS s -- incoming FROM apipayments - WHERE amount > 0 AND pending = 0 -- don't sum pending + WHERE amount > 0 AND pending = 0 -- don't sum pending GROUP BY wallet UNION ALL - SELECT wallet, sum(amount + fee) AS s -- outgoing, sum fees + SELECT wallet, SUM(amount + fee) AS s -- outgoing, sum fees FROM apipayments - WHERE amount < 0 -- do sum pending + WHERE amount < 0 -- do sum pending GROUP BY wallet ) GROUP BY wallet; diff --git a/lnbits/db.py b/lnbits/db.py index fd7b21cad..a43c92c52 100644 --- a/lnbits/db.py +++ b/lnbits/db.py @@ -1,9 +1,7 @@ import os import sqlite3 -from typing import Optional - -from .settings import DATABASE_PATH, LNBITS_PATH +from .settings import LNBITS_PATH, LNBITS_DATA_FOLDER class Database: @@ -35,11 +33,28 @@ class Database: self.connection.commit() -def open_db(db_path: str = DATABASE_PATH) -> Database: +def open_db(db_name: str = "database") -> Database: + db_path = os.path.join(LNBITS_DATA_FOLDER, f"{db_name}.sqlite3") return Database(db_path=db_path) -def open_ext_db(extension: Optional[str] = None) -> Database: - if extension: - return open_db(os.path.join(LNBITS_PATH, "extensions", extension, "database.sqlite3")) - return open_db(os.path.join(LNBITS_PATH, "extensions", "overview.sqlite3")) +def open_ext_db(extension_name: str) -> Database: + return open_db(f"ext_{extension_name}") + + +def init_databases() -> None: + """Creates the necessary databases if they don't exist already.""" + """TODO: see how we can deal with migrations.""" + + schemas = [ + ("database", os.path.join(LNBITS_PATH, "data", "schema.sql")), + ] + + for extension in [x[1] for x in os.walk(os.path.join(LNBITS_PATH, "extensions"))][0]: + schemas.append((f"ext_{extension}", os.path.join(LNBITS_PATH, "extensions", extension, "schema.sql"))) + + for schema in [s for s in schemas if os.path.exists(s[1])]: + with open_db(schema[0]) as db: + with open(schema[1]) as schemafile: + for stmt in schemafile.read().split(";\n\n"): + db.execute(stmt, []) diff --git a/lnbits/extensions/overview.sqlite3 b/lnbits/extensions/overview.sqlite3 deleted file mode 100644 index b4957a1b44d50bb0753aba783aa93b3ec4a24a36..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI%!Aiq09LMp*39`X9FCrcWu_pylK_9^Cq6kHBRuS2yV#LZ^q3zm}2YnGQK7{Y! z+xP@#N)Ljv?B?K`@TVmIUrP8~b9NfUxly-C`Vi%6M=S~{#hy|^2xF^eYtELmwQFnM zRyD?ZlZFwS@~UZ5vfUT$e(Pn@0s06afB*srAb$2}`ul-aUbXG1wMN|=WhV9Np}q_^>}oJHg&F3VS3f=p^^xwW zu0Hh7gHUaMx9^itmjBT$jmlrzjrCbG<-Q0YfB*srAb=J@57f|W6%kSz9I2^Pp-XaAt;UALj?%7` z_&1ok^*Vx zfB*y_009U<00Izzz+DvhUfH_0kje1fN1>|Jb=wU;*P5yw#3wVWMQJ-yIQB|j3M(GB z#9}&SebL{nRIcF2H?k-;in(>WSQcxtEbNl=HdmnBby;w-6xj-zR?3m@JDs+w_jPhwIk73fAw`&B*A zP4;mQOfJ&ibe1xg=vMuC7jIYW83GW100bZa0SG_<0uX=z1Rwx`i4-uXDsEmf^U^$< z$O2Im1Rwwb2tWV=5P$##AOHafKmYAznYsFK#i-2fKKPM7 zPd~nY7=3QOI5Hpr0SG_<0uX=z1a2uXTfOB1ls?A)G)G@#e{{&iMjT9RWn&k1K6d1k p2L74Sx`&r-K4rhD{Td!MjkElyr|Q$5^Z()WV)!ObhEGq`>|aMLpFRKp diff --git a/lnbits/extensions/withdraw/schema.sql b/lnbits/extensions/withdraw/schema.sql new file mode 100644 index 000000000..f0037fe37 --- /dev/null +++ b/lnbits/extensions/withdraw/schema.sql @@ -0,0 +1,18 @@ +CREATE TABLE IF NOT EXISTS withdraws ( + key INTEGER PRIMARY KEY AUTOINCREMENT, + usr TEXT, + wal TEXT, + walnme TEXT, + adm INTEGER, + uni TEXT, + tit TEXT, + maxamt INTEGER, + minamt INTEGER, + spent INTEGER, + inc INTEGER, + tme INTEGER, + uniq INTEGER DEFAULT 0, + withdrawals TEXT, + tmestmp INTEGER, + rand TEXT +); diff --git a/lnbits/extensions/withdraw/templates/withdraw/index.html b/lnbits/extensions/withdraw/templates/withdraw/index.html index b334a2b8f..c5ddc3e7f 100644 --- a/lnbits/extensions/withdraw/templates/withdraw/index.html +++ b/lnbits/extensions/withdraw/templates/withdraw/index.html @@ -34,9 +34,9 @@
    - {% if user_ext[0][3] %} + {% if "withdraw" in user_ext %}
  • - LNURLw
  • {% endif %} diff --git a/lnbits/extensions/withdraw/views.py b/lnbits/extensions/withdraw/views.py index ed30fa924..5babf7774 100644 --- a/lnbits/extensions/withdraw/views.py +++ b/lnbits/extensions/withdraw/views.py @@ -21,9 +21,8 @@ def index(): # 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,)) + user_ext = db.fetchall("SELECT * FROM extensions WHERE user = ?", (usr,)) + user_ext = [v[0] for v in user_ext] with open_ext_db("withdraw") as withdraw_ext_db: user_fau = withdraw_ext_db.fetchall("SELECT * FROM withdraws WHERE usr = ?", (usr,)) @@ -92,10 +91,18 @@ def create(): dt = datetime.now() seconds = dt.timestamp() + with open_db() as db: + user_ext = db.fetchall("SELECT * FROM extensions WHERE user = ?", (usr,)) + user_ext = [v[0] for v in user_ext] + # 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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + with open_ext_db("withdraw") as withdraw_ext_db: + withdraw_ext_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], @@ -115,14 +122,8 @@ def create(): ), ) - # 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 diff --git a/lnbits/extensions/withdraw/views_api.py b/lnbits/extensions/withdraw/views_api.py index b86b78b00..bac586e00 100644 --- a/lnbits/extensions/withdraw/views_api.py +++ b/lnbits/extensions/withdraw/views_api.py @@ -28,7 +28,6 @@ def api_lnurlencode(urlstr, parstr): 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 @@ -42,7 +41,6 @@ def api_lnurlfetch(parstr, urlstr, rand): 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: @@ -58,7 +56,6 @@ def api_lnurlfetch(parstr, urlstr, rand): default_description="LNbits LNURL withdraw", ) - return res.json(), 200 @@ -79,11 +76,9 @@ def api_lnurlwithdraw(rand): 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 @@ -110,8 +105,8 @@ def api_lnurlwithdraw(rand): header = {"Content-Type": "application/json", "Grpc-Metadata-macaroon": str(user_fau[0][4])} data = {"payment_request": pr} - #this works locally but not being served over host, bug, needs fixing - #r = requests.post(url="https://lnbits.com/api/v1/channels/transactions", headers=header, data=json.dumps(data)) + # this works locally but not being served over host, bug, needs fixing + # r = requests.post(url="https://lnbits.com/api/v1/channels/transactions", headers=header, data=json.dumps(data)) r = requests.post(url=url_for("api_transactions", _external=True), headers=header, data=json.dumps(data)) r_json = r.json() diff --git a/lnbits/helpers.py b/lnbits/helpers.py index b7c3646f4..eca91a643 100644 --- a/lnbits/helpers.py +++ b/lnbits/helpers.py @@ -3,12 +3,9 @@ import sqlite3 class MegaEncoder(json.JSONEncoder): - def default(self, o): - if type(o) == sqlite3.Row: - val = {} - for k in o.keys(): - val[k] = o[k] - return val + def default(self, obj): + if isinstance(obj, sqlite3.Row): + return {k: obj[k] for k in obj.keys()} return obj diff --git a/lnbits/settings.py b/lnbits/settings.py index 88c17457f..90b8cde6c 100644 --- a/lnbits/settings.py +++ b/lnbits/settings.py @@ -1,7 +1,7 @@ import os from .wallets import OpenNodeWallet # OR LndWallet OR OpennodeWallet - + WALLET = OpenNodeWallet(endpoint=os.getenv("OPENNODE_API_ENDPOINT"),admin_key=os.getenv("OPENNODE_ADMIN_KEY"),invoice_key=os.getenv("OPENNODE_INVOICE_KEY")) #WALLET = LntxbotWallet(endpoint=os.getenv("LNTXBOT_API_ENDPOINT"),admin_key=os.getenv("LNTXBOT_ADMIN_KEY"),invoice_key=os.getenv("LNTXBOT_INVOICE_KEY")) #WALLET = LndWallet(endpoint=os.getenv("LND_API_ENDPOINT"),admin_macaroon=os.getenv("LND_ADMIN_MACAROON"),invoice_macaroon=os.getenv("LND_INVOICE_MACAROON"),read_macaroon=os.getenv("LND_READ_MACAROON")) @@ -9,7 +9,7 @@ WALLET = OpenNodeWallet(endpoint=os.getenv("OPENNODE_API_ENDPOINT"),admin_key=os LNBITS_PATH = os.path.dirname(os.path.realpath(__file__)) -DATABASE_PATH = os.getenv("DATABASE_PATH", os.path.join(LNBITS_PATH, "data", "database.sqlite3")) +LNBITS_DATA_FOLDER = os.getenv("LNBITS_DATA_FOLDER", os.path.join(LNBITS_PATH, "data")) DEFAULT_USER_WALLET_NAME = os.getenv("DEFAULT_USER_WALLET_NAME", "Bitcoin LN Wallet") FEE_RESERVE = float(os.getenv("FEE_RESERVE", 0)) diff --git a/lnbits/templates/extensions.html b/lnbits/templates/extensions.html index 7d768b7d6..2c3b976a7 100644 --- a/lnbits/templates/extensions.html +++ b/lnbits/templates/extensions.html @@ -36,23 +36,23 @@
      - {% if user_ext[0][1] %} + {% if "lnevents" in user_ext %}
    • - LNEvents
    • {% endif %} - {% if user_ext[0][2] %} + {% if "lnjoust" in user_ext %}
    • - LNJoust
    • {% endif %} - {% if user_ext[0][3] %} + {% if "withdraw" in user_ext %}
    • - LNURLw
    • {% endif %} @@ -97,8 +97,7 @@
      - - {% if not user_ext[0][3] %} + {% if "withdraw" not in user_ext %}
      @@ -113,18 +112,18 @@
      - + Activate
      {% else %} - + {% endif %} @@ -159,7 +158,7 @@ window.user = {{ user | megajson | safe }} window.user_wallets = {{ user_wallets | megajson | safe }} window.user_ext = {{ user_ext | megajson | safe }} - +
      {% endblock %}