Browse Source

migration tracking.

aiosqlite
fiatjaf 4 years ago
committed by fiatjaf
parent
commit
c965bca41d
  1. 4
      Makefile
  2. 2
      docs/devs/extensions.md
  3. 3
      docs/devs/installation.md
  4. 5
      docs/guide/installation.md
  5. 52
      lnbits/__init__.py
  6. 72
      lnbits/core/migrations.py
  7. 8
      lnbits/extensions/amilk/migrations.py
  8. 8
      lnbits/extensions/diagonalley/migrations.py
  9. 9
      lnbits/extensions/events/migrations.py
  10. 5
      lnbits/extensions/example/migrations.py
  11. 9
      lnbits/extensions/lnticket/migrations.py
  12. 8
      lnbits/extensions/lnurlp/migrations.py
  13. 8
      lnbits/extensions/paywall/migrations.py
  14. 8
      lnbits/extensions/tpos/migrations.py
  15. 8
      lnbits/extensions/usermanager/migrations.py
  16. 9
      lnbits/extensions/withdraw/migrations.py
  17. 4
      main.py

4
Makefile

@ -8,7 +8,7 @@ prettier: $(shell find lnbits -name "*.js" -name ".html")
./node_modules/.bin/prettier --write lnbits/static/js/*.js lnbits/core/static/js/*.js lnbits/extensions/*/templates/*/*.html ./lnbits/core/templates/core/*.html lnbits/templates/*.html lnbits/extensions/*/static/js/*.js ./node_modules/.bin/prettier --write lnbits/static/js/*.js lnbits/core/static/js/*.js lnbits/extensions/*/templates/*/*.html ./lnbits/core/templates/core/*.html lnbits/templates/*.html lnbits/extensions/*/static/js/*.js
black: $(shell find lnbits -name "*.py") black: $(shell find lnbits -name "*.py")
./venv/bin/black --line-length 120 lnbits ./venv/bin/black lnbits
mypy: $(shell find lnbits -name "*.py") mypy: $(shell find lnbits -name "*.py")
./venv/bin/mypy lnbits ./venv/bin/mypy lnbits
@ -17,4 +17,4 @@ checkprettier: $(shell find lnbits -name "*.js" -name ".html")
./node_modules/.bin/prettier --check lnbits/static/js/*.js lnbits/core/static/js/*.js lnbits/extensions/*/templates/*/*.html ./lnbits/core/templates/core/*.html lnbits/templates/*.html lnbits/extensions/*/static/js/*.js ./node_modules/.bin/prettier --check lnbits/static/js/*.js lnbits/core/static/js/*.js lnbits/extensions/*/templates/*/*.html ./lnbits/core/templates/core/*.html lnbits/templates/*.html lnbits/extensions/*/static/js/*.js
checkblack: $(shell find lnbits -name "*.py") checkblack: $(shell find lnbits -name "*.py")
./venv/bin/black --check --line-length 120 lnbits ./venv/bin/black --check lnbits

2
docs/devs/extensions.md

@ -19,7 +19,7 @@ find . -type f -print0 | xargs -0 sed -i 's/example/mysuperplugin/g' # Change al
Going over the example extension's structure: Going over the example extension's structure:
* views_api.py: This is where your public API would go. It will be exposed at "$DOMAIN/$PLUGIN/$ROUTE". For example: https://lnbits.com/mysuperplugin/api/v1/tools. * views_api.py: This is where your public API would go. It will be exposed at "$DOMAIN/$PLUGIN/$ROUTE". For example: https://lnbits.com/mysuperplugin/api/v1/tools.
* views.py: The `/` path will show up as your plugin's home page in lnbits' UI. Other pages you can define yourself. The `templates` folder should explain itself in relation to this. * views.py: The `/` path will show up as your plugin's home page in lnbits' UI. Other pages you can define yourself. The `templates` folder should explain itself in relation to this.
* migrations.py: Create database tables for your plugin. They'll be created when you run `pipenv run flask migrate`. * migrations.py: Create database tables for your plugin. They'll be created automatically when you start lnbits.
... This document is a work-in-progress. Send pull requests if you get stuck, so others don't. ... This document is a work-in-progress. Send pull requests if you get stuck, so others don't.

3
docs/devs/installation.md

@ -45,8 +45,7 @@ Running the server
LNbits uses [Flask][flask] as an application server. LNbits uses [Flask][flask] as an application server.
```sh ```sh
$ pipenv run flask migrate $ pipenv run python main.py
$ pipenv 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`

5
docs/guide/installation.md

@ -19,11 +19,10 @@ $ source ./.venv/bin/activate
You will need to set the variables in `.env.example`, and rename the file to `.env`. You will need to set the variables in `.env.example`, and rename the file to `.env`.
Run the migrations and the Flask server: Run the server:
```sh ```sh
(.venv) $ flask migrate (.venv) $ python main.py
(.venv) $ flask run
``` ```
You might also need to install additional packages, depending on the [backend wallet](./wallets.md) you use. You might also need to install additional packages, depending on the [backend wallet](./wallets.md) you use.

52
lnbits/__init__.py

@ -1,4 +1,6 @@
import re
import importlib import importlib
import sqlite3
from flask import Flask from flask import Flask
from flask_assets import Environment, Bundle # type: ignore from flask_assets import Environment, Bundle # type: ignore
@ -8,9 +10,10 @@ from flask_talisman import Talisman # type: ignore
from os import getenv from os import getenv
from werkzeug.middleware.proxy_fix import ProxyFix from werkzeug.middleware.proxy_fix import ProxyFix
from .core import core_app, migrations as core_migrations from .core import core_app
from .helpers import ExtensionManager from .helpers import ExtensionManager
from .settings import FORCE_HTTPS from .settings import FORCE_HTTPS
from .db import open_db, open_ext_db
disabled_extensions = getenv("LNBITS_DISABLED_EXTENSIONS", "").split(",") disabled_extensions = getenv("LNBITS_DISABLED_EXTENSIONS", "").split(",")
@ -73,21 +76,40 @@ assets.register("base_css", Bundle("scss/base.scss", filters="pyscss", output="c
# -------- # --------
@app.cli.command("migrate")
def migrate_databases(): def migrate_databases():
"""Creates the necessary databases if they don't exist already; or migrates them.""" """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.")
from .core import migrations as core_migrations
# init with open_db() as core_db:
# ---- try:
rows = core_db.fetchall("SELECT * FROM dbversions")
if __name__ == "__main__": except sqlite3.OperationalError:
app.run() # migration 3 wasn't ran
core_migrations.m000_create_migrations_table(core_db)
rows = core_db.fetchall("SELECT * FROM dbversions")
current_versions = {row["db"]: row["version"] for row in rows}
matcher = re.compile(r"^m(\d\d\d)_")
def run_migration(db, migrations_module):
db_name = migrations_module.__name__.split(".")[-2]
for key, run_migration in migrations_module.__dict__.items():
if match := matcher.match(key):
version = int(match.group(1))
if version > current_versions.get(db_name, 0):
print(f"running migration {db_name}.{version}")
run_migration(db)
core_db.execute(
"INSERT OR REPLACE INTO dbversions (db, version) VALUES (?, ?)", (db_name, version)
)
run_migration(core_db, core_migrations)
for ext in valid_extensions:
try:
ext_migrations = importlib.import_module(f"lnbits.extensions.{ext.code}.migrations")
with open_ext_db(ext.code) as db:
run_migration(db, ext_migrations)
except ImportError:
raise ImportError(f"Please make sure that the extension `{ext.code}` has a migrations file.")

72
lnbits/core/migrations.py

@ -1,4 +1,15 @@
from lnbits.db import open_db import sqlite3
def m000_create_migrations_table(db):
db.execute(
"""
CREATE TABLE dbversions (
db TEXT PRIMARY KEY,
version INT NOT NULL
)
"""
)
def m001_initial(db): def m001_initial(db):
@ -76,35 +87,36 @@ def m002_add_fields_to_apipayments(db):
Adding fields to apipayments for better accounting, Adding fields to apipayments for better accounting,
and renaming payhash to checking_id since that is what it really is. and renaming payhash to checking_id since that is what it really is.
""" """
db.execute("ALTER TABLE apipayments RENAME COLUMN payhash TO checking_id") try:
db.execute("ALTER TABLE apipayments ADD COLUMN hash TEXT") db.execute("ALTER TABLE apipayments RENAME COLUMN payhash TO checking_id")
db.execute("CREATE INDEX by_hash ON apipayments (hash)") db.execute("ALTER TABLE apipayments ADD COLUMN hash TEXT")
db.execute("ALTER TABLE apipayments ADD COLUMN preimage TEXT") db.execute("CREATE INDEX by_hash ON apipayments (hash)")
db.execute("ALTER TABLE apipayments ADD COLUMN bolt11 TEXT") db.execute("ALTER TABLE apipayments ADD COLUMN preimage TEXT")
db.execute("ALTER TABLE apipayments ADD COLUMN extra TEXT") db.execute("ALTER TABLE apipayments ADD COLUMN bolt11 TEXT")
db.execute("ALTER TABLE apipayments ADD COLUMN extra TEXT")
import json
rows = db.fetchall("SELECT * FROM apipayments")
for row in rows:
if not row["memo"] or not row["memo"].startswith("#"):
continue
for ext in ["withdraw", "events", "lnticket", "paywall", "tpos"]: import json
prefix = "#" + ext + " "
if row["memo"].startswith(prefix):
new = row["memo"][len(prefix) :]
db.execute(
"""
UPDATE apipayments SET extra = ?, memo = ?
WHERE checking_id = ? AND memo = ?
""",
(json.dumps({"tag": ext}), new, row["checking_id"], row["memo"]),
)
break
rows = db.fetchall("SELECT * FROM apipayments")
for row in rows:
if not row["memo"] or not row["memo"].startswith("#"):
continue
def migrate(): for ext in ["withdraw", "events", "lnticket", "paywall", "tpos"]:
with open_db() as db: prefix = "#" + ext + " "
m001_initial(db) if row["memo"].startswith(prefix):
m002_add_fields_to_apipayments(db) new = row["memo"][len(prefix) :]
db.execute(
"""
UPDATE apipayments SET extra = ?, memo = ?
WHERE checking_id = ? AND memo = ?
""",
(json.dumps({"tag": ext}), new, row["checking_id"], row["memo"]),
)
break
except sqlite3.OperationalError:
# this is necessary now because it may be the case that this migration will
# run twice in some environments.
# catching errors like this won't be necessary in anymore now that we
# keep track of db versions so no migration ever runs twice.
pass

8
lnbits/extensions/amilk/migrations.py

@ -1,6 +1,3 @@
from lnbits.db import open_ext_db
def m001_initial(db): def m001_initial(db):
""" """
Initial amilks table. Initial amilks table.
@ -16,8 +13,3 @@ def m001_initial(db):
); );
""" """
) )
def migrate():
with open_ext_db("amilk") as db:
m001_initial(db)

8
lnbits/extensions/diagonalley/migrations.py

@ -1,6 +1,3 @@
from lnbits.db import open_ext_db
def m001_initial(db): def m001_initial(db):
""" """
Initial products table. Initial products table.
@ -61,8 +58,3 @@ def m001_initial(db):
); );
""" """
) )
def migrate():
with open_ext_db("diagonalley") as db:
m001_initial(db)

9
lnbits/extensions/events/migrations.py

@ -1,6 +1,3 @@
from lnbits.db import open_ext_db
def m001_initial(db): def m001_initial(db):
db.execute( db.execute(
@ -86,9 +83,3 @@ def m002_changed(db):
), ),
) )
db.execute("DROP TABLE tickets") db.execute("DROP TABLE tickets")
def migrate():
with open_ext_db("events") as db:
m001_initial(db)
m002_changed(db)

5
lnbits/extensions/example/migrations.py

@ -1,5 +0,0 @@
from lnbits.db import open_ext_db
def migrate():
print("pending")

9
lnbits/extensions/lnticket/migrations.py

@ -1,6 +1,3 @@
from lnbits.db import open_ext_db
def m001_initial(db): def m001_initial(db):
db.execute( db.execute(
@ -86,9 +83,3 @@ def m002_changed(db):
), ),
) )
db.execute("DROP TABLE tickets") db.execute("DROP TABLE tickets")
def migrate():
with open_ext_db("lnticket") as db:
m001_initial(db)
m002_changed(db)

8
lnbits/extensions/lnurlp/migrations.py

@ -1,6 +1,3 @@
from lnbits.db import open_ext_db
def m001_initial(db): def m001_initial(db):
""" """
Initial pay table. Initial pay table.
@ -17,8 +14,3 @@ def m001_initial(db):
); );
""" """
) )
def migrate():
with open_ext_db("lnurlp") as db:
m001_initial(db)

8
lnbits/extensions/paywall/migrations.py

@ -1,7 +1,5 @@
from sqlite3 import OperationalError from sqlite3 import OperationalError
from lnbits.db import open_ext_db
def m001_initial(db): def m001_initial(db):
""" """
@ -65,9 +63,3 @@ def m002_redux(db):
) )
db.execute("DROP TABLE paywalls_old") db.execute("DROP TABLE paywalls_old")
def migrate():
with open_ext_db("paywall") as db:
m001_initial(db)
m002_redux(db)

8
lnbits/extensions/tpos/migrations.py

@ -1,6 +1,3 @@
from lnbits.db import open_ext_db
def m001_initial(db): def m001_initial(db):
""" """
Initial tposs table. Initial tposs table.
@ -15,8 +12,3 @@ def m001_initial(db):
); );
""" """
) )
def migrate():
with open_ext_db("tpos") as db:
m001_initial(db)

8
lnbits/extensions/usermanager/migrations.py

@ -1,6 +1,3 @@
from lnbits.db import open_ext_db
def m001_initial(db): def m001_initial(db):
""" """
Initial users table. Initial users table.
@ -32,8 +29,3 @@ def m001_initial(db):
); );
""" """
) )
def migrate():
with open_ext_db("usermanager") as db:
m001_initial(db)

9
lnbits/extensions/withdraw/migrations.py

@ -1,6 +1,3 @@
from lnbits.db import open_ext_db
def m001_initial(db): def m001_initial(db):
""" """
Creates an improved withdraw table and migrates the existing data. Creates an improved withdraw table and migrates the existing data.
@ -97,9 +94,3 @@ def m002_change_withdraw_table(db):
), ),
) )
db.execute("DROP TABLE withdraw_links") db.execute("DROP TABLE withdraw_links")
def migrate():
with open_ext_db("withdraw") as db:
m001_initial(db)
m002_change_withdraw_table(db)

4
main.py

@ -0,0 +1,4 @@
from lnbits import app, migrate_databases
migrate_databases()
app.run()
Loading…
Cancel
Save