801 lines
25 KiB
Python
801 lines
25 KiB
Python
import argparse
|
|
import os
|
|
import sqlite3
|
|
|
|
import psycopg2
|
|
from environs import Env # type: ignore
|
|
|
|
env = Env()
|
|
env.read_env()
|
|
|
|
# Python script to migrate an LNbits SQLite DB to Postgres
|
|
# All credits to @Fritz446 for the awesome work
|
|
|
|
|
|
# pip install psycopg2 OR psycopg2-binary
|
|
|
|
|
|
# Change these values as needed
|
|
|
|
|
|
sqfolder = "data/"
|
|
|
|
LNBITS_DATABASE_URL = env.str("LNBITS_DATABASE_URL", default=None)
|
|
if LNBITS_DATABASE_URL is None:
|
|
pgdb = "lnbits"
|
|
pguser = "lnbits"
|
|
pgpswd = "postgres"
|
|
pghost = "localhost"
|
|
pgport = "5432"
|
|
pgschema = ""
|
|
else:
|
|
# parse postgres://lnbits:postgres@localhost:5432/lnbits
|
|
pgdb = LNBITS_DATABASE_URL.split("/")[-1]
|
|
pguser = LNBITS_DATABASE_URL.split("@")[0].split(":")[-2][2:]
|
|
pgpswd = LNBITS_DATABASE_URL.split("@")[0].split(":")[-1]
|
|
pghost = LNBITS_DATABASE_URL.split("@")[1].split(":")[0]
|
|
pgport = LNBITS_DATABASE_URL.split("@")[1].split(":")[1].split("/")[0]
|
|
pgschema = ""
|
|
|
|
|
|
def get_sqlite_cursor(sqdb) -> sqlite3:
|
|
consq = sqlite3.connect(sqdb)
|
|
return consq.cursor()
|
|
|
|
|
|
def get_postgres_cursor():
|
|
conpg = psycopg2.connect(
|
|
database=pgdb, user=pguser, password=pgpswd, host=pghost, port=pgport
|
|
)
|
|
return conpg.cursor()
|
|
|
|
|
|
def check_db_versions(sqdb):
|
|
sqlite = get_sqlite_cursor(sqdb)
|
|
dblite = dict(sqlite.execute("SELECT * FROM dbversions;").fetchall())
|
|
sqlite.close()
|
|
|
|
postgres = get_postgres_cursor()
|
|
postgres.execute("SELECT * FROM public.dbversions;")
|
|
dbpost = dict(postgres.fetchall())
|
|
|
|
for key in dblite.keys():
|
|
if key in dblite and key in dbpost and dblite[key] != dbpost[key]:
|
|
raise Exception(
|
|
f"sqlite database version ({dblite[key]}) of {key} doesn't match postgres database version {dbpost[key]}"
|
|
)
|
|
|
|
connection = postgres.connection
|
|
postgres.close()
|
|
connection.close()
|
|
|
|
print("Database versions OK, converting")
|
|
|
|
|
|
def fix_id(seq, values):
|
|
if not values or len(values) == 0:
|
|
return
|
|
|
|
postgres = get_postgres_cursor()
|
|
|
|
max_id = values[len(values) - 1][0]
|
|
postgres.execute(f"SELECT setval('{seq}', {max_id});")
|
|
|
|
connection = postgres.connection
|
|
postgres.close()
|
|
connection.close()
|
|
|
|
|
|
def insert_to_pg(query, data):
|
|
if len(data) == 0:
|
|
return
|
|
|
|
cursor = get_postgres_cursor()
|
|
connection = cursor.connection
|
|
|
|
for d in data:
|
|
try:
|
|
cursor.execute(query, d)
|
|
except Exception as e:
|
|
if args.ignore_errors:
|
|
print(e)
|
|
print(f"Failed to insert {d}")
|
|
else:
|
|
print("query:", query)
|
|
print("data:", d)
|
|
raise ValueError(f"Failed to insert {d}")
|
|
connection.commit()
|
|
|
|
cursor.close()
|
|
connection.close()
|
|
|
|
|
|
def migrate_core(sqlite_db_file):
|
|
sq = get_sqlite_cursor(sqlite_db_file)
|
|
|
|
# ACCOUNTS
|
|
res = sq.execute("SELECT * FROM accounts;")
|
|
q = f"INSERT INTO public.accounts (id, email, pass) VALUES (%s, %s, %s);"
|
|
insert_to_pg(q, res.fetchall())
|
|
|
|
# WALLETS
|
|
res = sq.execute("SELECT * FROM wallets;")
|
|
q = f'INSERT INTO public.wallets (id, name, "user", adminkey, inkey) VALUES (%s, %s, %s, %s, %s);'
|
|
insert_to_pg(q, res.fetchall())
|
|
|
|
# API PAYMENTS
|
|
res = sq.execute("SELECT * FROM apipayments;")
|
|
q = f"""
|
|
INSERT INTO public.apipayments(
|
|
checking_id, amount, fee, wallet, pending, memo, "time", hash, preimage, bolt11, extra, webhook, webhook_status)
|
|
VALUES (%s, %s, %s, %s, %s::boolean, %s, to_timestamp(%s), %s, %s, %s, %s, %s, %s);
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
|
|
# BALANCE CHECK
|
|
res = sq.execute("SELECT * FROM balance_check;")
|
|
q = f"INSERT INTO public.balance_check(wallet, service, url) VALUES (%s, %s, %s);"
|
|
insert_to_pg(q, res.fetchall())
|
|
|
|
# BALANCE NOTIFY
|
|
res = sq.execute("SELECT * FROM balance_notify;")
|
|
q = f"INSERT INTO public.balance_notify(wallet, url) VALUES (%s, %s);"
|
|
insert_to_pg(q, res.fetchall())
|
|
|
|
# EXTENSIONS
|
|
res = sq.execute("SELECT * FROM extensions;")
|
|
q = f'INSERT INTO public.extensions("user", extension, active) VALUES (%s, %s, %s::boolean);'
|
|
insert_to_pg(q, res.fetchall())
|
|
|
|
print("Migrated: core")
|
|
|
|
|
|
def migrate_ext(sqlite_db_file, schema, ignore_missing=True):
|
|
|
|
# skip this file it has been moved to ext_lnurldevices.sqlite3
|
|
if sqlite_db_file == "data/ext_lnurlpos.sqlite3":
|
|
return
|
|
|
|
print(f"Migrating {sqlite_db_file}.{schema}")
|
|
sq = get_sqlite_cursor(sqlite_db_file)
|
|
if schema == "bleskomat":
|
|
# BLESKOMAT LNURLS
|
|
res = sq.execute("SELECT * FROM bleskomat_lnurls;")
|
|
q = f"""
|
|
INSERT INTO bleskomat.bleskomat_lnurls(
|
|
id, bleskomat, wallet, hash, tag, params, api_key_id, initial_uses, remaining_uses, created_time, updated_time)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s);
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
|
|
# BLESKOMATS
|
|
res = sq.execute("SELECT * FROM bleskomats;")
|
|
q = f"""
|
|
INSERT INTO bleskomat.bleskomats(
|
|
id, wallet, api_key_id, api_key_secret, api_key_encoding, name, fiat_currency, exchange_rate_provider, fee)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s);
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
elif schema == "captcha":
|
|
# CAPTCHA
|
|
res = sq.execute("SELECT * FROM captchas;")
|
|
q = f"""
|
|
INSERT INTO captcha.captchas(
|
|
id, wallet, url, memo, description, amount, "time", remembers, extras)
|
|
VALUES (%s, %s, %s, %s, %s, %s, to_timestamp(%s), %s, %s);
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
elif schema == "copilot":
|
|
# OLD COPILOTS
|
|
res = sq.execute("SELECT * FROM copilots;")
|
|
q = f"""
|
|
INSERT INTO copilot.copilots(
|
|
id, "user", title, lnurl_toggle, wallet, animation1, animation2, animation3, animation1threshold, animation2threshold, animation3threshold, animation1webhook, animation2webhook, animation3webhook, lnurl_title, show_message, show_ack, show_price, amount_made, fullscreen_cam, iframe_url, "timestamp")
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, to_timestamp(%s));
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
|
|
# NEW COPILOTS
|
|
q = f"""
|
|
INSERT INTO copilot.newer_copilots(
|
|
id, "user", title, lnurl_toggle, wallet, animation1, animation2, animation3, animation1threshold, animation2threshold, animation3threshold, animation1webhook, animation2webhook, animation3webhook, lnurl_title, show_message, show_ack, show_price, amount_made, fullscreen_cam, iframe_url, "timestamp")
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, to_timestamp(%s));
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
elif schema == "events":
|
|
# EVENTS
|
|
res = sq.execute("SELECT * FROM events;")
|
|
q = f"""
|
|
INSERT INTO events.events(
|
|
id, wallet, name, info, closing_date, event_start_date, event_end_date, amount_tickets, price_per_ticket, sold, "time")
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, to_timestamp(%s));
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
# EVENT TICKETS
|
|
res = sq.execute("SELECT * FROM ticket;")
|
|
q = f"""
|
|
INSERT INTO events.ticket(
|
|
id, wallet, event, name, email, registered, paid, "time")
|
|
VALUES (%s, %s, %s, %s, %s, %s::boolean, %s::boolean, to_timestamp(%s));
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
elif schema == "example":
|
|
# Example doesn't have a database at the moment
|
|
pass
|
|
elif schema == "hivemind":
|
|
# Hivemind doesn't have a database at the moment
|
|
pass
|
|
elif schema == "jukebox":
|
|
# JUKEBOXES
|
|
res = sq.execute("SELECT * FROM jukebox;")
|
|
q = f"""
|
|
INSERT INTO jukebox.jukebox(
|
|
id, "user", title, wallet, inkey, sp_user, sp_secret, sp_access_token, sp_refresh_token, sp_device, sp_playlists, price, profit)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s);
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
# JUKEBOX PAYMENTS
|
|
res = sq.execute("SELECT * FROM jukebox_payment;")
|
|
q = f"""
|
|
INSERT INTO jukebox.jukebox_payment(
|
|
payment_hash, juke_id, song_id, paid)
|
|
VALUES (%s, %s, %s, %s::boolean);
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
elif schema == "withdraw":
|
|
# WITHDRAW LINK
|
|
res = sq.execute("SELECT * FROM withdraw_link;")
|
|
q = f"""
|
|
INSERT INTO withdraw.withdraw_link (
|
|
id,
|
|
wallet,
|
|
title,
|
|
min_withdrawable,
|
|
max_withdrawable,
|
|
uses,
|
|
wait_time,
|
|
is_unique,
|
|
unique_hash,
|
|
k1,
|
|
open_time,
|
|
used,
|
|
usescsv,
|
|
webhook_url,
|
|
custom_url
|
|
)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s);
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
# WITHDRAW HASH CHECK
|
|
res = sq.execute("SELECT * FROM hash_check;")
|
|
q = f"""
|
|
INSERT INTO withdraw.hash_check (id, lnurl_id)
|
|
VALUES (%s, %s);
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
elif schema == "watchonly":
|
|
# WALLETS
|
|
res = sq.execute("SELECT * FROM wallets;")
|
|
q = f"""
|
|
INSERT INTO watchonly.wallets (
|
|
id,
|
|
"user",
|
|
masterpub,
|
|
title,
|
|
address_no,
|
|
balance,
|
|
type,
|
|
fingerprint,
|
|
network
|
|
)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s);
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
# ADDRESSES
|
|
res = sq.execute("SELECT * FROM addresses;")
|
|
q = f"""
|
|
INSERT INTO watchonly.addresses (id, address, wallet, amount, branch_index, address_index, has_activity, note)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s::boolean, %s);
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
# CONFIG
|
|
res = sq.execute("SELECT * FROM config;")
|
|
q = f"""
|
|
INSERT INTO watchonly.config ("user", json_data)
|
|
VALUES (%s, %s);
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
elif schema == "usermanager":
|
|
# USERS
|
|
res = sq.execute("SELECT * FROM users;")
|
|
q = f"""
|
|
INSERT INTO usermanager.users (id, name, admin, email, password)
|
|
VALUES (%s, %s, %s, %s, %s);
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
# WALLETS
|
|
res = sq.execute("SELECT * FROM wallets;")
|
|
q = f"""
|
|
INSERT INTO usermanager.wallets (id, admin, name, "user", adminkey, inkey)
|
|
VALUES (%s, %s, %s, %s, %s, %s);
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
elif schema == "tpos":
|
|
# TPOSS
|
|
res = sq.execute("SELECT * FROM tposs;")
|
|
q = f"""
|
|
INSERT INTO tpos.tposs (id, wallet, name, currency, tip_wallet, tip_options)
|
|
VALUES (%s, %s, %s, %s, %s, %s);
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
elif schema == "tipjar":
|
|
# TIPJARS
|
|
res = sq.execute("SELECT * FROM TipJars;")
|
|
q = f"""
|
|
INSERT INTO tipjar.TipJars (id, name, wallet, onchain, webhook)
|
|
VALUES (%s, %s, %s, %s, %s);
|
|
"""
|
|
pay_links = res.fetchall()
|
|
insert_to_pg(q, pay_links)
|
|
fix_id("tipjar.tipjars_id_seq", pay_links)
|
|
# TIPS
|
|
res = sq.execute("SELECT * FROM Tips;")
|
|
q = f"""
|
|
INSERT INTO tipjar.Tips (id, wallet, name, message, sats, tipjar)
|
|
VALUES (%s, %s, %s, %s, %s, %s);
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
elif schema == "subdomains":
|
|
# DOMAIN
|
|
res = sq.execute("SELECT * FROM domain;")
|
|
q = f"""
|
|
INSERT INTO subdomains.domain (
|
|
id,
|
|
wallet,
|
|
domain,
|
|
webhook,
|
|
cf_token,
|
|
cf_zone_id,
|
|
description,
|
|
cost,
|
|
amountmade,
|
|
allowed_record_types,
|
|
time
|
|
)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, to_timestamp(%s));
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
# SUBDOMAIN
|
|
res = sq.execute("SELECT * FROM subdomain;")
|
|
q = f"""
|
|
INSERT INTO subdomains.subdomain (
|
|
id,
|
|
domain,
|
|
email,
|
|
subdomain,
|
|
ip,
|
|
wallet,
|
|
sats,
|
|
duration,
|
|
paid,
|
|
record_type,
|
|
time
|
|
)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s::boolean, %s, to_timestamp(%s));
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
elif schema == "streamalerts":
|
|
# SERVICES
|
|
res = sq.execute("SELECT * FROM Services;")
|
|
q = f"""
|
|
INSERT INTO streamalerts.Services (
|
|
id,
|
|
state,
|
|
twitchuser,
|
|
client_id,
|
|
client_secret,
|
|
wallet,
|
|
onchain,
|
|
servicename,
|
|
authenticated,
|
|
token
|
|
)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s::boolean, %s);
|
|
"""
|
|
services = res.fetchall()
|
|
insert_to_pg(q, services)
|
|
fix_id("streamalerts.services_id_seq", services)
|
|
# DONATIONS
|
|
res = sq.execute("SELECT * FROM Donations;")
|
|
q = f"""
|
|
INSERT INTO streamalerts.Donations (
|
|
id,
|
|
wallet,
|
|
name,
|
|
message,
|
|
cur_code,
|
|
sats,
|
|
amount,
|
|
service,
|
|
posted,
|
|
)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s::boolean);
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
elif schema == "splitpayments":
|
|
# TARGETS
|
|
res = sq.execute("SELECT * FROM targets;")
|
|
q = f"""
|
|
INSERT INTO splitpayments.targets (wallet, source, percent, alias)
|
|
VALUES (%s, %s, %s, %s);
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
elif schema == "satspay":
|
|
# CHARGES
|
|
res = sq.execute("SELECT * FROM charges;")
|
|
q = f"""
|
|
INSERT INTO satspay.charges (
|
|
id,
|
|
"user",
|
|
description,
|
|
onchainwallet,
|
|
onchainaddress,
|
|
lnbitswallet,
|
|
payment_request,
|
|
payment_hash,
|
|
webhook,
|
|
completelink,
|
|
completelinktext,
|
|
time,
|
|
amount,
|
|
balance,
|
|
timestamp
|
|
)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, to_timestamp(%s));
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
elif schema == "satsdice":
|
|
# SATSDICE PAY
|
|
res = sq.execute("SELECT * FROM satsdice_pay;")
|
|
q = f"""
|
|
INSERT INTO satsdice.satsdice_pay (
|
|
id,
|
|
wallet,
|
|
title,
|
|
min_bet,
|
|
max_bet,
|
|
amount,
|
|
served_meta,
|
|
served_pr,
|
|
multiplier,
|
|
haircut,
|
|
chance,
|
|
base_url,
|
|
open_time
|
|
)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s);
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
# SATSDICE WITHDRAW
|
|
res = sq.execute("SELECT * FROM satsdice_withdraw;")
|
|
q = f"""
|
|
INSERT INTO satsdice.satsdice_withdraw (
|
|
id,
|
|
satsdice_pay,
|
|
value,
|
|
unique_hash,
|
|
k1,
|
|
open_time,
|
|
used
|
|
)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s);
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
# SATSDICE PAYMENT
|
|
res = sq.execute("SELECT * FROM satsdice_payment;")
|
|
q = f"""
|
|
INSERT INTO satsdice.satsdice_payment (
|
|
payment_hash,
|
|
satsdice_pay,
|
|
value,
|
|
paid,
|
|
lost
|
|
)
|
|
VALUES (%s, %s, %s, %s::boolean, %s::boolean);
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
# SATSDICE HASH CHECK
|
|
res = sq.execute("SELECT * FROM hash_checkw;")
|
|
q = f"""
|
|
INSERT INTO satsdice.hash_checkw (id, lnurl_id)
|
|
VALUES (%s, %s);
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
elif schema == "paywall":
|
|
# PAYWALLS
|
|
res = sq.execute("SELECT * FROM paywalls;")
|
|
q = f"""
|
|
INSERT INTO paywall.paywalls(
|
|
id,
|
|
wallet,
|
|
url,
|
|
memo,
|
|
description,
|
|
amount,
|
|
time,
|
|
remembers,
|
|
extras
|
|
)
|
|
VALUES (%s, %s, %s, %s, %s, %s, to_timestamp(%s), %s, %s);
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
elif schema == "offlineshop":
|
|
# SHOPS
|
|
res = sq.execute("SELECT * FROM shops;")
|
|
q = f"""
|
|
INSERT INTO offlineshop.shops (id, wallet, method, wordlist)
|
|
VALUES (%s, %s, %s, %s);
|
|
"""
|
|
shops = res.fetchall()
|
|
insert_to_pg(q, shops)
|
|
fix_id("offlineshop.shops_id_seq", shops)
|
|
# ITEMS
|
|
res = sq.execute("SELECT * FROM items;")
|
|
q = f"""
|
|
INSERT INTO offlineshop.items (shop, id, name, description, image, enabled, price, unit, fiat_base_multiplier)
|
|
VALUES (%s, %s, %s, %s, %s, %s::boolean, %s, %s, %s);
|
|
"""
|
|
items = res.fetchall()
|
|
insert_to_pg(q, items)
|
|
fix_id("offlineshop.items_id_seq", items)
|
|
elif schema == "lnurlpos" or schema == "lnurldevice":
|
|
# lnurldevice
|
|
res = sq.execute("SELECT * FROM lnurldevices;")
|
|
q = f"""
|
|
INSERT INTO lnurldevice.lnurldevices (id, key, title, wallet, currency, device, profit, timestamp)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, to_timestamp(%s));
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
# lnurldevice PAYMENT
|
|
res = sq.execute("SELECT * FROM lnurldevicepayment;")
|
|
q = f"""
|
|
INSERT INTO lnurldevice.lnurldevicepayment (id, deviceid, payhash, payload, pin, sats, timestamp)
|
|
VALUES (%s, %s, %s, %s, %s, %s, to_timestamp(%s));
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
elif schema == "lnurlp":
|
|
# PAY LINKS
|
|
res = sq.execute("SELECT * FROM pay_links;")
|
|
q = f"""
|
|
INSERT INTO lnurlp.pay_links (
|
|
id,
|
|
wallet,
|
|
description,
|
|
min,
|
|
served_meta,
|
|
served_pr,
|
|
webhook_url,
|
|
success_text,
|
|
success_url,
|
|
currency,
|
|
comment_chars,
|
|
max,
|
|
fiat_base_multiplier
|
|
)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s);
|
|
"""
|
|
pay_links = res.fetchall()
|
|
insert_to_pg(q, pay_links)
|
|
fix_id("lnurlp.pay_links_id_seq", pay_links)
|
|
elif schema == "lndhub":
|
|
# LndHub doesn't have a database at the moment
|
|
pass
|
|
elif schema == "lnticket":
|
|
# TICKET
|
|
res = sq.execute("SELECT * FROM ticket;")
|
|
q = f"""
|
|
INSERT INTO lnticket.ticket (
|
|
id,
|
|
form,
|
|
email,
|
|
ltext,
|
|
name,
|
|
wallet,
|
|
sats,
|
|
paid,
|
|
time
|
|
)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s::boolean, to_timestamp(%s));
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
# FORM
|
|
res = sq.execute("SELECT * FROM form2;")
|
|
q = f"""
|
|
INSERT INTO lnticket.form2 (
|
|
id,
|
|
wallet,
|
|
name,
|
|
webhook,
|
|
description,
|
|
flatrate,
|
|
amount,
|
|
amountmade,
|
|
time
|
|
)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, to_timestamp(%s));
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
elif schema == "livestream":
|
|
# LIVESTREAMS
|
|
res = sq.execute("SELECT * FROM livestreams;")
|
|
q = f"""
|
|
INSERT INTO livestream.livestreams (
|
|
id,
|
|
wallet,
|
|
fee_pct,
|
|
current_track
|
|
)
|
|
VALUES (%s, %s, %s, %s);
|
|
"""
|
|
livestreams = res.fetchall()
|
|
insert_to_pg(q, livestreams)
|
|
fix_id("livestream.livestreams_id_seq", livestreams)
|
|
# PRODUCERS
|
|
res = sq.execute("SELECT * FROM producers;")
|
|
q = f"""
|
|
INSERT INTO livestream.producers (
|
|
livestream,
|
|
id,
|
|
"user",
|
|
wallet,
|
|
name
|
|
)
|
|
VALUES (%s, %s, %s, %s, %s);
|
|
"""
|
|
producers = res.fetchall()
|
|
insert_to_pg(q, producers)
|
|
fix_id("livestream.producers_id_seq", producers)
|
|
# TRACKS
|
|
res = sq.execute("SELECT * FROM tracks;")
|
|
q = f"""
|
|
INSERT INTO livestream.tracks (
|
|
livestream,
|
|
id,
|
|
download_url,
|
|
price_msat,
|
|
name,
|
|
producer
|
|
)
|
|
VALUES (%s, %s, %s, %s, %s, %s);
|
|
"""
|
|
tracks = res.fetchall()
|
|
insert_to_pg(q, tracks)
|
|
fix_id("livestream.tracks_id_seq", tracks)
|
|
elif schema == "lnaddress":
|
|
# DOMAINS
|
|
res = sq.execute("SELECT * FROM domain;")
|
|
q = f"""
|
|
INSERT INTO lnaddress.domain(
|
|
id, wallet, domain, webhook, cf_token, cf_zone_id, cost, "time")
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, to_timestamp(%s));
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
# ADDRESSES
|
|
res = sq.execute("SELECT * FROM address;")
|
|
q = f"""
|
|
INSERT INTO lnaddress.address(
|
|
id, wallet, domain, email, username, wallet_key, wallet_endpoint, sats, duration, paid, "time")
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s::boolean, to_timestamp(%s));
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
elif schema == "discordbot":
|
|
# USERS
|
|
res = sq.execute("SELECT * FROM users;")
|
|
q = f"""
|
|
INSERT INTO discordbot.users(
|
|
id, name, admin, discord_id)
|
|
VALUES (%s, %s, %s, %s);
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
# WALLETS
|
|
res = sq.execute("SELECT * FROM wallets;")
|
|
q = f"""
|
|
INSERT INTO discordbot.wallets(
|
|
id, admin, name, "user", adminkey, inkey)
|
|
VALUES (%s, %s, %s, %s, %s, %s);
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
elif schema == "scrub":
|
|
# SCRUB LINKS
|
|
res = sq.execute("SELECT * FROM scrub_links;")
|
|
q = f"""
|
|
INSERT INTO scrub.scrub_links (
|
|
id,
|
|
wallet,
|
|
description,
|
|
payoraddress
|
|
)
|
|
VALUES (%s, %s, %s, %s);
|
|
"""
|
|
insert_to_pg(q, res.fetchall())
|
|
else:
|
|
print(f"❌ Not implemented: {schema}")
|
|
sq.close()
|
|
|
|
if ignore_missing == False:
|
|
raise Exception(
|
|
f"Not implemented: {schema}. Use --ignore-missing to skip missing extensions."
|
|
)
|
|
return
|
|
|
|
print(f"✅ Migrated: {schema}")
|
|
sq.close()
|
|
|
|
|
|
parser = argparse.ArgumentParser(
|
|
description="LNbits migration tool for migrating data from SQLite to PostgreSQL"
|
|
)
|
|
parser.add_argument(
|
|
dest="sqlite_path",
|
|
const=True,
|
|
nargs="?",
|
|
help=f"SQLite DB folder *or* single extension db file to migrate. Default: {sqfolder}",
|
|
default=sqfolder,
|
|
type=str,
|
|
)
|
|
parser.add_argument(
|
|
"-e",
|
|
"--extensions-only",
|
|
help="Migrate only extensions",
|
|
required=False,
|
|
default=False,
|
|
action="store_true",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"-s",
|
|
"--skip-missing",
|
|
help="Error if migration is missing for an extension",
|
|
required=False,
|
|
default=False,
|
|
action="store_true",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"-i",
|
|
"--ignore-errors",
|
|
help="Don't error if migration fails",
|
|
required=False,
|
|
default=False,
|
|
action="store_true",
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
print("Selected path: ", args.sqlite_path)
|
|
|
|
if os.path.isdir(args.sqlite_path):
|
|
file = os.path.join(args.sqlite_path, "database.sqlite3")
|
|
check_db_versions(file)
|
|
if not args.extensions_only:
|
|
print(f"Migrating: {file}")
|
|
migrate_core(file)
|
|
|
|
if os.path.isdir(args.sqlite_path):
|
|
files = [
|
|
os.path.join(args.sqlite_path, file) for file in os.listdir(args.sqlite_path)
|
|
]
|
|
else:
|
|
files = [args.sqlite_path]
|
|
|
|
for file in files:
|
|
filename = os.path.basename(file)
|
|
if filename.startswith("ext_"):
|
|
schema = filename.replace("ext_", "").split(".")[0]
|
|
print(f"Migrating: {file}")
|
|
migrate_ext(
|
|
file,
|
|
schema,
|
|
ignore_missing=args.skip_missing,
|
|
)
|