refactor: improve database migrations
This commit is contained in:
parent
6651122bb6
commit
490e166f75
17 changed files with 192 additions and 119 deletions
|
|
@ -61,6 +61,7 @@ You will need to set the variables in .env.example, and rename the file to .env
|
||||||
Running the server
|
Running the server
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
$ flask migrate
|
||||||
$ flask run
|
$ flask run
|
||||||
|
|
||||||
There is an environment variable called `FLASK_ENV` that has to be set to `development`
|
There is an environment variable called `FLASK_ENV` that has to be set to `development`
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,7 @@ from flask_compress import Compress
|
||||||
from flask_talisman import Talisman
|
from flask_talisman import Talisman
|
||||||
from os import getenv
|
from os import getenv
|
||||||
|
|
||||||
from .core import core_app
|
from .core import core_app, migrations as core_migrations
|
||||||
from .db import init_databases
|
|
||||||
from .helpers import ExtensionManager, megajson
|
from .helpers import ExtensionManager, megajson
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -70,14 +69,24 @@ assets.url = app.static_url_path
|
||||||
assets.register("base_css", Bundle("scss/base.scss", filters="pyscss", output="css/base.css"))
|
assets.register("base_css", Bundle("scss/base.scss", filters="pyscss", output="css/base.css"))
|
||||||
|
|
||||||
|
|
||||||
|
# commands
|
||||||
|
# --------
|
||||||
|
|
||||||
|
@app.cli.command("migrate")
|
||||||
|
def migrate_databases():
|
||||||
|
"""Creates the necessary databases if they don't exist already; or migrates them."""
|
||||||
|
core_migrations.migrate()
|
||||||
|
|
||||||
|
for ext in valid_extensions:
|
||||||
|
try:
|
||||||
|
ext_migrations = importlib.import_module(f"lnbits.extensions.{ext.code}.migrations")
|
||||||
|
ext_migrations.migrate()
|
||||||
|
except Exception:
|
||||||
|
raise ImportError(f"Please make sure that the extension `{ext.code}` has a migrations file.")
|
||||||
|
|
||||||
|
|
||||||
# init
|
# init
|
||||||
# ----
|
# ----
|
||||||
|
|
||||||
|
|
||||||
@app.before_first_request
|
|
||||||
def init():
|
|
||||||
init_databases()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app.run()
|
app.run()
|
||||||
|
|
|
||||||
65
lnbits/core/migrations.py
Normal file
65
lnbits/core/migrations.py
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
from lnbits.db import open_db
|
||||||
|
|
||||||
|
|
||||||
|
def m001_initial(db):
|
||||||
|
"""
|
||||||
|
Initial LNbits tables.
|
||||||
|
"""
|
||||||
|
db.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS accounts (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
email TEXT,
|
||||||
|
pass TEXT
|
||||||
|
);
|
||||||
|
""")
|
||||||
|
db.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS extensions (
|
||||||
|
user TEXT NOT NULL,
|
||||||
|
extension TEXT NOT NULL,
|
||||||
|
active BOOLEAN DEFAULT 0,
|
||||||
|
|
||||||
|
UNIQUE (user, extension)
|
||||||
|
);
|
||||||
|
""")
|
||||||
|
db.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS wallets (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
user TEXT NOT NULL,
|
||||||
|
adminkey TEXT NOT NULL,
|
||||||
|
inkey TEXT
|
||||||
|
);
|
||||||
|
""")
|
||||||
|
db.execute("""
|
||||||
|
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')),
|
||||||
|
|
||||||
|
UNIQUE (wallet, payhash)
|
||||||
|
);
|
||||||
|
""")
|
||||||
|
db.execute("""
|
||||||
|
CREATE VIEW IF NOT EXISTS balances AS
|
||||||
|
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
|
||||||
|
GROUP BY wallet
|
||||||
|
UNION ALL
|
||||||
|
SELECT wallet, SUM(amount + fee) AS s -- outgoing, sum fees
|
||||||
|
FROM apipayments
|
||||||
|
WHERE amount < 0 -- do sum pending
|
||||||
|
GROUP BY wallet
|
||||||
|
)
|
||||||
|
GROUP BY wallet;
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
def migrate():
|
||||||
|
with open_db() as db:
|
||||||
|
m001_initial(db)
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
CREATE TABLE IF NOT EXISTS accounts (
|
|
||||||
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
|
|
||||||
);
|
|
||||||
|
|
||||||
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')),
|
|
||||||
|
|
||||||
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
|
|
||||||
FROM apipayments
|
|
||||||
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
|
|
||||||
FROM apipayments
|
|
||||||
WHERE amount < 0 -- do sum pending
|
|
||||||
GROUP BY wallet
|
|
||||||
)
|
|
||||||
GROUP BY wallet;
|
|
||||||
28
lnbits/db.py
28
lnbits/db.py
|
|
@ -1,8 +1,7 @@
|
||||||
import os
|
import os
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
from .helpers import ExtensionManager
|
from .settings import LNBITS_DATA_FOLDER
|
||||||
from .settings import LNBITS_PATH, LNBITS_DATA_FOLDER
|
|
||||||
|
|
||||||
|
|
||||||
class Database:
|
class Database:
|
||||||
|
|
@ -19,16 +18,16 @@ class Database:
|
||||||
self.cursor.close()
|
self.cursor.close()
|
||||||
self.connection.close()
|
self.connection.close()
|
||||||
|
|
||||||
def fetchall(self, query: str, values: tuple) -> list:
|
def fetchall(self, query: str, values: tuple = ()) -> list:
|
||||||
"""Given a query, return cursor.fetchall() rows."""
|
"""Given a query, return cursor.fetchall() rows."""
|
||||||
self.cursor.execute(query, values)
|
self.cursor.execute(query, values)
|
||||||
return self.cursor.fetchall()
|
return self.cursor.fetchall()
|
||||||
|
|
||||||
def fetchone(self, query: str, values: tuple):
|
def fetchone(self, query: str, values: tuple = ()):
|
||||||
self.cursor.execute(query, values)
|
self.cursor.execute(query, values)
|
||||||
return self.cursor.fetchone()
|
return self.cursor.fetchone()
|
||||||
|
|
||||||
def execute(self, query: str, values: tuple) -> None:
|
def execute(self, query: str, values: tuple = ()) -> None:
|
||||||
"""Given a query, cursor.execute() it."""
|
"""Given a query, cursor.execute() it."""
|
||||||
self.cursor.execute(query, values)
|
self.cursor.execute(query, values)
|
||||||
self.connection.commit()
|
self.connection.commit()
|
||||||
|
|
@ -41,22 +40,3 @@ def open_db(db_name: str = "database") -> Database:
|
||||||
|
|
||||||
def open_ext_db(extension_name: str) -> Database:
|
def open_ext_db(extension_name: str) -> Database:
|
||||||
return open_db(f"ext_{extension_name}")
|
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, "core", "schema.sql")),
|
|
||||||
]
|
|
||||||
|
|
||||||
for extension in ExtensionManager().extensions:
|
|
||||||
extension_path = os.path.join(LNBITS_PATH, "extensions", extension.code)
|
|
||||||
schemas.append((f"ext_{extension.code}", os.path.join(extension_path, "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, [])
|
|
||||||
|
|
|
||||||
21
lnbits/extensions/amilk/migrations.py
Normal file
21
lnbits/extensions/amilk/migrations.py
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
from lnbits.db import open_ext_db
|
||||||
|
|
||||||
|
|
||||||
|
def m001_initial(db):
|
||||||
|
"""
|
||||||
|
Initial amilks table.
|
||||||
|
"""
|
||||||
|
db.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS amilks (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
wallet TEXT NOT NULL,
|
||||||
|
lnurl TEXT NOT NULL,
|
||||||
|
atime TEXT NOT NULL,
|
||||||
|
amount INTEGER NOT NULL
|
||||||
|
);
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
def migrate():
|
||||||
|
with open_ext_db("amilk") as db:
|
||||||
|
m001_initial(db)
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
CREATE TABLE IF NOT EXISTS amilks (
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
wallet TEXT NOT NULL,
|
|
||||||
lnurl TEXT NOT NULL,
|
|
||||||
atime TEXT NOT NULL,
|
|
||||||
amount INTEGER NOT NULL
|
|
||||||
);
|
|
||||||
5
lnbits/extensions/example/migrations.py
Normal file
5
lnbits/extensions/example/migrations.py
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
from lnbits.db import open_ext_db
|
||||||
|
|
||||||
|
|
||||||
|
def migrate():
|
||||||
|
print("pending")
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
/* create your extensions table and the variables needed here */
|
|
||||||
22
lnbits/extensions/paywall/migrations.py
Normal file
22
lnbits/extensions/paywall/migrations.py
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
from lnbits.db import open_ext_db
|
||||||
|
|
||||||
|
|
||||||
|
def m001_initial(db):
|
||||||
|
"""
|
||||||
|
Initial paywalls table.
|
||||||
|
"""
|
||||||
|
db.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS paywalls (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
wallet TEXT NOT NULL,
|
||||||
|
url TEXT NOT NULL,
|
||||||
|
memo TEXT NOT NULL,
|
||||||
|
amount INTEGER NOT NULL,
|
||||||
|
time TIMESTAMP NOT NULL DEFAULT (strftime('%s', 'now'))
|
||||||
|
);
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
def migrate():
|
||||||
|
with open_ext_db("tpos") as db:
|
||||||
|
m001_initial(db)
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
CREATE TABLE IF NOT EXISTS paywalls (
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
wallet TEXT NOT NULL,
|
|
||||||
url TEXT NOT NULL,
|
|
||||||
memo TEXT NOT NULL,
|
|
||||||
amount INTEGER NOT NULL,
|
|
||||||
time TIMESTAMP NOT NULL DEFAULT (strftime('%s', 'now'))
|
|
||||||
);
|
|
||||||
20
lnbits/extensions/tpos/migrations.py
Normal file
20
lnbits/extensions/tpos/migrations.py
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
from lnbits.db import open_ext_db
|
||||||
|
|
||||||
|
|
||||||
|
def m001_initial(db):
|
||||||
|
"""
|
||||||
|
Initial tposs table.
|
||||||
|
"""
|
||||||
|
db.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS tposs (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
wallet TEXT NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
currency TEXT NOT NULL
|
||||||
|
);
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
def migrate():
|
||||||
|
with open_ext_db("tpos") as db:
|
||||||
|
m001_initial(db)
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
CREATE TABLE IF NOT EXISTS tposs (
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
wallet TEXT NOT NULL,
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
currency TEXT NOT NULL
|
|
||||||
);
|
|
||||||
37
lnbits/extensions/withdraw/migrations.py
Normal file
37
lnbits/extensions/withdraw/migrations.py
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
from datetime import datetime
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
|
from lnbits.db import open_ext_db
|
||||||
|
|
||||||
|
|
||||||
|
def m001_initial(db):
|
||||||
|
"""
|
||||||
|
Initial withdraw table.
|
||||||
|
"""
|
||||||
|
db.execute(
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def migrate():
|
||||||
|
with open_ext_db("withdraw") as db:
|
||||||
|
m001_initial(db)
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
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
|
|
||||||
);
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue