mirror of
https://github.com/lnbits/lnbits-legend.git
synced 2024-11-20 10:39:59 +01:00
Updated Withdraw extension to handle unique facets
This commit is contained in:
parent
6c286cd265
commit
294beb2fca
@ -1,6 +1,6 @@
|
||||
from datetime import datetime
|
||||
from typing import List, Optional, Union
|
||||
|
||||
import shortuuid # type: ignore
|
||||
from lnbits.db import open_ext_db
|
||||
from lnbits.helpers import urlsafe_short_hash
|
||||
|
||||
@ -17,6 +17,7 @@ def create_withdraw_link(
|
||||
wait_time: int,
|
||||
is_unique: bool,
|
||||
) -> WithdrawLink:
|
||||
|
||||
with open_ext_db("withdraw") as db:
|
||||
link_id = urlsafe_short_hash()
|
||||
db.execute(
|
||||
@ -32,9 +33,10 @@ def create_withdraw_link(
|
||||
is_unique,
|
||||
unique_hash,
|
||||
k1,
|
||||
open_time
|
||||
open_time,
|
||||
usescsv
|
||||
)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
link_id,
|
||||
@ -48,24 +50,36 @@ def create_withdraw_link(
|
||||
urlsafe_short_hash(),
|
||||
urlsafe_short_hash(),
|
||||
int(datetime.now().timestamp()) + wait_time,
|
||||
usescsv,
|
||||
),
|
||||
)
|
||||
|
||||
return get_withdraw_link(link_id)
|
||||
return get_withdraw_link(link_id, uses)
|
||||
|
||||
|
||||
def get_withdraw_link(link_id: str) -> Optional[WithdrawLink]:
|
||||
def get_withdraw_link(link_id: str, num=None) -> Optional[WithdrawLink]:
|
||||
with open_ext_db("withdraw") as db:
|
||||
row = db.fetchone("SELECT * FROM withdraw_links WHERE id = ?", (link_id,))
|
||||
|
||||
return WithdrawLink.from_row(row) if row else None
|
||||
link = []
|
||||
for item in row:
|
||||
link.append(item)
|
||||
tohash = row["id"] + row["unique_hash"] + str(num)
|
||||
link.append(shortuuid.uuid(name=tohash))
|
||||
return WithdrawLink._make(link)
|
||||
|
||||
|
||||
def get_withdraw_link_by_hash(unique_hash: str) -> Optional[WithdrawLink]:
|
||||
def get_withdraw_link_by_hash(unique_hash: str, num=None) -> Optional[WithdrawLink]:
|
||||
with open_ext_db("withdraw") as db:
|
||||
row = db.fetchone("SELECT * FROM withdraw_links WHERE unique_hash = ?", (unique_hash,))
|
||||
|
||||
return WithdrawLink.from_row(row) if row else None
|
||||
link = []
|
||||
for item in row:
|
||||
link.append(item)
|
||||
if not num:
|
||||
link.append("")
|
||||
return WithdrawLink._make(link)
|
||||
tohash = row["id"] + row["unique_hash"] + str(num)
|
||||
link.append(shortuuid.uuid(name=tohash))
|
||||
return WithdrawLink._make(link)
|
||||
|
||||
|
||||
|
||||
def get_withdraw_links(wallet_ids: Union[str, List[str]]) -> List[WithdrawLink]:
|
||||
@ -75,13 +89,15 @@ def get_withdraw_links(wallet_ids: Union[str, List[str]]) -> List[WithdrawLink]:
|
||||
with open_ext_db("withdraw") as db:
|
||||
q = ",".join(["?"] * len(wallet_ids))
|
||||
rows = db.fetchall(f"SELECT * FROM withdraw_links WHERE wallet IN ({q})", (*wallet_ids,))
|
||||
|
||||
return [WithdrawLink.from_row(row) for row in rows]
|
||||
|
||||
links = []
|
||||
for x in rows:
|
||||
links.append(WithdrawLink.from_row(row) for row in rows)
|
||||
return links
|
||||
|
||||
|
||||
def update_withdraw_link(link_id: str, **kwargs) -> Optional[WithdrawLink]:
|
||||
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
|
||||
|
||||
with open_ext_db("withdraw") as db:
|
||||
db.execute(f"UPDATE withdraw_links SET {q} WHERE id = ?", (*kwargs.values(), link_id))
|
||||
row = db.fetchone("SELECT * FROM withdraw_links WHERE id = ?", (link_id,))
|
||||
@ -92,3 +108,7 @@ def update_withdraw_link(link_id: str, **kwargs) -> Optional[WithdrawLink]:
|
||||
def delete_withdraw_link(link_id: str) -> None:
|
||||
with open_ext_db("withdraw") as db:
|
||||
db.execute("DELETE FROM withdraw_links WHERE id = ?", (link_id,))
|
||||
|
||||
def chunks(lst, n):
|
||||
for i in range(0, len(lst), n):
|
||||
yield lst[i:i + n]
|
@ -5,34 +5,6 @@ from lnbits.helpers import urlsafe_short_hash
|
||||
|
||||
|
||||
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 m002_change_withdraw_table(db):
|
||||
"""
|
||||
Creates an improved withdraw table and migrates the existing data.
|
||||
"""
|
||||
@ -50,52 +22,13 @@ def m002_change_withdraw_table(db):
|
||||
unique_hash TEXT UNIQUE,
|
||||
k1 TEXT,
|
||||
open_time INTEGER,
|
||||
used INTEGER DEFAULT 0
|
||||
used INTEGER DEFAULT 0,
|
||||
usescsv TEXT
|
||||
);
|
||||
"""
|
||||
)
|
||||
db.execute("CREATE INDEX IF NOT EXISTS wallet_idx ON withdraw_links (wallet)")
|
||||
db.execute("CREATE UNIQUE INDEX IF NOT EXISTS unique_hash_idx ON withdraw_links (unique_hash)")
|
||||
|
||||
for row in [list(row) for row in db.fetchall("SELECT * FROM withdraws")]:
|
||||
db.execute(
|
||||
"""
|
||||
INSERT INTO withdraw_links (
|
||||
id,
|
||||
wallet,
|
||||
title,
|
||||
min_withdrawable,
|
||||
max_withdrawable,
|
||||
uses,
|
||||
wait_time,
|
||||
is_unique,
|
||||
unique_hash,
|
||||
k1,
|
||||
open_time,
|
||||
used
|
||||
)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
row[5], # uni
|
||||
row[2], # wal
|
||||
row[6], # tit
|
||||
row[8], # minamt
|
||||
row[7], # maxamt
|
||||
row[10], # inc
|
||||
row[11], # tme
|
||||
row[12], # uniq
|
||||
urlsafe_short_hash(),
|
||||
urlsafe_short_hash(),
|
||||
int(datetime.now().timestamp()) + row[11],
|
||||
row[9], # spent
|
||||
),
|
||||
)
|
||||
|
||||
db.execute("DROP TABLE withdraws")
|
||||
|
||||
""")
|
||||
|
||||
|
||||
def migrate():
|
||||
with open_ext_db("withdraw") as db:
|
||||
m001_initial(db)
|
||||
m002_change_withdraw_table(db)
|
||||
|
||||
|
@ -19,11 +19,14 @@ class WithdrawLink(NamedTuple):
|
||||
k1: str
|
||||
open_time: int
|
||||
used: int
|
||||
usescsv: str
|
||||
multihash: str
|
||||
|
||||
@classmethod
|
||||
def from_row(cls, row: Row) -> "WithdrawLink":
|
||||
data = dict(row)
|
||||
data["is_unique"] = bool(data["is_unique"])
|
||||
data["multihash"] = ""
|
||||
return cls(**data)
|
||||
|
||||
@property
|
||||
@ -33,14 +36,20 @@ class WithdrawLink(NamedTuple):
|
||||
@property
|
||||
def lnurl(self) -> Lnurl:
|
||||
scheme = "https" if FORCE_HTTPS else None
|
||||
url = url_for("withdraw.api_lnurl_response", unique_hash=self.unique_hash, _external=True, _scheme=scheme)
|
||||
print(self.is_unique)
|
||||
if self.is_unique:
|
||||
url = url_for("withdraw.api_lnurl_multi_response", unique_hash=self.unique_hash, id_unique_hash=self.multihash, _external=True, _scheme=scheme)
|
||||
else:
|
||||
url = url_for("withdraw.api_lnurl_response", unique_hash=self.unique_hash, _external=True, _scheme=scheme)
|
||||
|
||||
return lnurl_encode(url)
|
||||
|
||||
@property
|
||||
def lnurl_response(self) -> LnurlWithdrawResponse:
|
||||
scheme = "https" if FORCE_HTTPS else None
|
||||
url = url_for("withdraw.api_lnurl_callback", unique_hash=self.unique_hash, _external=True, _scheme=scheme)
|
||||
|
||||
url = url_for("withdraw.api_lnurl_callback", unique_hash=self.unique_hash, _external=True, _scheme=scheme)
|
||||
print(url)
|
||||
return LnurlWithdrawResponse(
|
||||
callback=url,
|
||||
k1=self.k1,
|
||||
|
@ -1,16 +1,24 @@
|
||||
Vue.component(VueQrcode.name, VueQrcode);
|
||||
Vue.component(VueQrcode.name, VueQrcode)
|
||||
|
||||
var locationPath = [window.location.protocol, '//', window.location.host, window.location.pathname].join('');
|
||||
var locationPath = [
|
||||
window.location.protocol,
|
||||
'//',
|
||||
window.location.host,
|
||||
window.location.pathname
|
||||
].join('')
|
||||
|
||||
var mapWithdrawLink = function (obj) {
|
||||
obj._data = _.clone(obj);
|
||||
obj.date = Quasar.utils.date.formatDate(new Date(obj.time * 1000), 'YYYY-MM-DD HH:mm');
|
||||
obj.min_fsat = new Intl.NumberFormat(LOCALE).format(obj.min_withdrawable);
|
||||
obj.max_fsat = new Intl.NumberFormat(LOCALE).format(obj.max_withdrawable);
|
||||
obj.uses_left = obj.uses - obj.used;
|
||||
obj.print_url = [locationPath, 'print/', obj.id].join('');
|
||||
obj.withdraw_url = [locationPath, obj.id].join('');
|
||||
return obj;
|
||||
obj._data = _.clone(obj)
|
||||
obj.date = Quasar.utils.date.formatDate(
|
||||
new Date(obj.time * 1000),
|
||||
'YYYY-MM-DD HH:mm'
|
||||
)
|
||||
obj.min_fsat = new Intl.NumberFormat(LOCALE).format(obj.min_withdrawable)
|
||||
obj.max_fsat = new Intl.NumberFormat(LOCALE).format(obj.max_withdrawable)
|
||||
obj.uses_left = obj.uses - obj.used
|
||||
obj.print_url = [locationPath, 'print/', obj.id].join('')
|
||||
obj.withdraw_url = [locationPath, obj.id].join('')
|
||||
return obj
|
||||
}
|
||||
|
||||
new Vue({
|
||||
@ -24,8 +32,18 @@ new Vue({
|
||||
columns: [
|
||||
{name: 'id', align: 'left', label: 'ID', field: 'id'},
|
||||
{name: 'title', align: 'left', label: 'Title', field: 'title'},
|
||||
{name: 'wait_time', align: 'right', label: 'Wait', field: 'wait_time'},
|
||||
{name: 'uses_left', align: 'right', label: 'Uses left', field: 'uses_left'},
|
||||
{
|
||||
name: 'wait_time',
|
||||
align: 'right',
|
||||
label: 'Wait',
|
||||
field: 'wait_time'
|
||||
},
|
||||
{
|
||||
name: 'uses_left',
|
||||
align: 'right',
|
||||
label: 'Uses left',
|
||||
field: 'uses_left'
|
||||
},
|
||||
{name: 'min', align: 'right', label: 'Min (sat)', field: 'min_fsat'},
|
||||
{name: 'max', align: 'right', label: 'Max (sat)', field: 'max_fsat'}
|
||||
],
|
||||
@ -45,118 +63,146 @@ new Vue({
|
||||
show: false,
|
||||
data: null
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
sortedWithdrawLinks: function () {
|
||||
return this.withdrawLinks.sort(function (a, b) {
|
||||
return b.uses_left - a.uses_left;
|
||||
});
|
||||
return b.uses_left - a.uses_left
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getWithdrawLinks: function () {
|
||||
var self = this;
|
||||
var self = this
|
||||
|
||||
LNbits.api.request(
|
||||
'GET',
|
||||
'/withdraw/api/v1/links?all_wallets',
|
||||
this.g.user.wallets[0].inkey
|
||||
).then(function (response) {
|
||||
self.withdrawLinks = response.data.map(function (obj) {
|
||||
return mapWithdrawLink(obj);
|
||||
});
|
||||
}).catch(function (error) {
|
||||
clearInterval(self.checker);
|
||||
LNbits.utils.notifyApiError(error);
|
||||
});
|
||||
LNbits.api
|
||||
.request(
|
||||
'GET',
|
||||
'/withdraw/api/v1/links?all_wallets',
|
||||
this.g.user.wallets[0].inkey
|
||||
)
|
||||
.then(function (response) {
|
||||
self.withdrawLinks = response.data.map(function (obj) {
|
||||
return mapWithdrawLink(obj)
|
||||
})
|
||||
})
|
||||
.catch(function (error) {
|
||||
clearInterval(self.checker)
|
||||
LNbits.utils.notifyApiError(error)
|
||||
})
|
||||
},
|
||||
closeFormDialog: function () {
|
||||
this.formDialog.data = {
|
||||
is_unique: false
|
||||
};
|
||||
}
|
||||
},
|
||||
openQrCodeDialog: function (linkId) {
|
||||
var link = _.findWhere(this.withdrawLinks, {id: linkId});
|
||||
this.qrCodeDialog.data = _.clone(link);
|
||||
this.qrCodeDialog.show = true;
|
||||
var link = _.findWhere(this.withdrawLinks, {id: linkId})
|
||||
this.qrCodeDialog.data = _.clone(link)
|
||||
this.qrCodeDialog.show = true
|
||||
},
|
||||
openUpdateDialog: function (linkId) {
|
||||
var link = _.findWhere(this.withdrawLinks, {id: linkId});
|
||||
this.formDialog.data = _.clone(link._data);
|
||||
this.formDialog.show = true;
|
||||
var link = _.findWhere(this.withdrawLinks, {id: linkId})
|
||||
this.formDialog.data = _.clone(link._data)
|
||||
this.formDialog.show = true
|
||||
},
|
||||
sendFormData: function () {
|
||||
var wallet = _.findWhere(this.g.user.wallets, {id: this.formDialog.data.wallet});
|
||||
var data = _.omit(this.formDialog.data, 'wallet');
|
||||
var wallet = _.findWhere(this.g.user.wallets, {
|
||||
id: this.formDialog.data.wallet
|
||||
})
|
||||
var data = _.omit(this.formDialog.data, 'wallet')
|
||||
|
||||
data.wait_time = data.wait_time * {
|
||||
'seconds': 1,
|
||||
'minutes': 60,
|
||||
'hours': 3600
|
||||
}[this.formDialog.secondMultiplier];
|
||||
data.wait_time =
|
||||
data.wait_time *
|
||||
{
|
||||
seconds: 1,
|
||||
minutes: 60,
|
||||
hours: 3600
|
||||
}[this.formDialog.secondMultiplier]
|
||||
|
||||
if (data.id) { this.updateWithdrawLink(wallet, data); }
|
||||
else { this.createWithdrawLink(wallet, data); }
|
||||
if (data.id) {
|
||||
this.updateWithdrawLink(wallet, data)
|
||||
} else {
|
||||
this.createWithdrawLink(wallet, data)
|
||||
}
|
||||
},
|
||||
updateWithdrawLink: function (wallet, data) {
|
||||
var self = this;
|
||||
var self = this
|
||||
|
||||
LNbits.api.request(
|
||||
'PUT',
|
||||
'/withdraw/api/v1/links/' + data.id,
|
||||
wallet.adminkey,
|
||||
_.pick(data, 'title', 'min_withdrawable', 'max_withdrawable', 'uses', 'wait_time', 'is_unique')
|
||||
).then(function (response) {
|
||||
self.withdrawLinks = _.reject(self.withdrawLinks, function (obj) { return obj.id == data.id; });
|
||||
self.withdrawLinks.push(mapWithdrawLink(response.data));
|
||||
self.formDialog.show = false;
|
||||
}).catch(function (error) {
|
||||
LNbits.utils.notifyApiError(error);
|
||||
});
|
||||
LNbits.api
|
||||
.request(
|
||||
'PUT',
|
||||
'/withdraw/api/v1/links/' + data.id,
|
||||
wallet.adminkey,
|
||||
_.pick(
|
||||
data,
|
||||
'title',
|
||||
'min_withdrawable',
|
||||
'max_withdrawable',
|
||||
'uses',
|
||||
'wait_time',
|
||||
'is_unique'
|
||||
)
|
||||
)
|
||||
.then(function (response) {
|
||||
self.withdrawLinks = _.reject(self.withdrawLinks, function (obj) {
|
||||
return obj.id == data.id
|
||||
})
|
||||
self.withdrawLinks.push(mapWithdrawLink(response.data))
|
||||
self.formDialog.show = false
|
||||
})
|
||||
.catch(function (error) {
|
||||
LNbits.utils.notifyApiError(error)
|
||||
})
|
||||
},
|
||||
createWithdrawLink: function (wallet, data) {
|
||||
var self = this;
|
||||
var self = this
|
||||
|
||||
LNbits.api.request(
|
||||
'POST',
|
||||
'/withdraw/api/v1/links',
|
||||
wallet.adminkey,
|
||||
data
|
||||
).then(function (response) {
|
||||
self.withdrawLinks.push(mapWithdrawLink(response.data));
|
||||
self.formDialog.show = false;
|
||||
}).catch(function (error) {
|
||||
LNbits.utils.notifyApiError(error);
|
||||
});
|
||||
LNbits.api
|
||||
.request('POST', '/withdraw/api/v1/links', wallet.adminkey, data)
|
||||
.then(function (response) {
|
||||
self.withdrawLinks.push(mapWithdrawLink(response.data))
|
||||
self.formDialog.show = false
|
||||
})
|
||||
.catch(function (error) {
|
||||
LNbits.utils.notifyApiError(error)
|
||||
})
|
||||
},
|
||||
deleteWithdrawLink: function (linkId) {
|
||||
var self = this;
|
||||
var link = _.findWhere(this.withdrawLinks, {id: linkId});
|
||||
var self = this
|
||||
var link = _.findWhere(this.withdrawLinks, {id: linkId})
|
||||
|
||||
LNbits.utils.confirmDialog(
|
||||
'Are you sure you want to delete this withdraw link?'
|
||||
).onOk(function () {
|
||||
LNbits.api.request(
|
||||
'DELETE',
|
||||
'/withdraw/api/v1/links/' + linkId,
|
||||
_.findWhere(self.g.user.wallets, {id: link.wallet}).adminkey
|
||||
).then(function (response) {
|
||||
self.withdrawLinks = _.reject(self.withdrawLinks, function (obj) { return obj.id == linkId; });
|
||||
}).catch(function (error) {
|
||||
LNbits.utils.notifyApiError(error);
|
||||
});
|
||||
});
|
||||
LNbits.utils
|
||||
.confirmDialog('Are you sure you want to delete this withdraw link?')
|
||||
.onOk(function () {
|
||||
LNbits.api
|
||||
.request(
|
||||
'DELETE',
|
||||
'/withdraw/api/v1/links/' + linkId,
|
||||
_.findWhere(self.g.user.wallets, {id: link.wallet}).adminkey
|
||||
)
|
||||
.then(function (response) {
|
||||
self.withdrawLinks = _.reject(self.withdrawLinks, function (obj) {
|
||||
return obj.id == linkId
|
||||
})
|
||||
})
|
||||
.catch(function (error) {
|
||||
LNbits.utils.notifyApiError(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
exportCSV: function () {
|
||||
LNbits.utils.exportCSV(this.paywallsTable.columns, this.paywalls);
|
||||
LNbits.utils.exportCSV(this.paywallsTable.columns, this.paywalls)
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
if (this.g.user.wallets.length) {
|
||||
var getWithdrawLinks = this.getWithdrawLinks;
|
||||
getWithdrawLinks();
|
||||
this.checker = setInterval(function () { getWithdrawLinks(); }, 20000);
|
||||
var getWithdrawLinks = this.getWithdrawLinks
|
||||
getWithdrawLinks()
|
||||
this.checker = setInterval(function () {
|
||||
getWithdrawLinks()
|
||||
}, 20000)
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
|
@ -45,13 +45,22 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %} {% block scripts %}
|
||||
|
||||
<script src="{{ url_for('static', filename='vendor/vue-qrcode@1.0.2/vue-qrcode.min.js') }}"></script>
|
||||
<script>
|
||||
Vue.component(VueQrcode.name, VueQrcode)
|
||||
|
||||
new Vue({
|
||||
el: '#vue',
|
||||
mixins: [windowMixin]
|
||||
mixins: [windowMixin],
|
||||
data: function () {
|
||||
return {
|
||||
theurl: location.host,
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@ -274,7 +274,6 @@
|
||||
>Shareable link</q-btn
|
||||
>
|
||||
<q-btn
|
||||
v-if="!qrCodeDialog.data.is_unique"
|
||||
outline
|
||||
color="grey"
|
||||
icon="print"
|
||||
|
@ -1,58 +1,79 @@
|
||||
{% extends "print.html" %} {% block page %}
|
||||
<!DOCTYPE html>
|
||||
{% block page %}
|
||||
|
||||
<div class="row justify-center">
|
||||
<div class="col-12 col-sm-8 col-lg-6 text-center">
|
||||
{% for i in range(link.uses) %}
|
||||
<div class="zimbabwe">
|
||||
<div class="qr">
|
||||
<qrcode value="{{ link.lnurl }}" :options="{width: 150}"></qrcode>
|
||||
<br /><br />
|
||||
<strong>{{ SITE_TITLE }}</strong><br />
|
||||
<strong>{{ link.max_withdrawable }} FREE SATS</strong><br />
|
||||
<small>Scan and follow link<br />or use Lightning wallet</small>
|
||||
</div>
|
||||
<img src="{{ url_for('static', filename='images/note.jpg') }}" />
|
||||
</div>
|
||||
<div class="col-12 col-sm-8 col-lg-6 text-center" id="vue">
|
||||
{% for page in link %}
|
||||
<page size="A4" id="pdfprint">
|
||||
<table style="width: 100%;">
|
||||
{% for threes in page %}
|
||||
<tr style="height: 37.125mm;">
|
||||
{% for one in threes %}
|
||||
<td style="width: 52.5mm;">
|
||||
<center>
|
||||
<h3 class="q-mb-md"></h3>
|
||||
<qrcode
|
||||
:value="theurl + '/lnurlwallet?lightning={{one.lnurl}}'"
|
||||
:options="{width: 110}"
|
||||
></qrcode>
|
||||
</center>
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</page>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %} {% block styles %}
|
||||
<style>
|
||||
.zimbabwe {
|
||||
page-break-inside: avoid;
|
||||
height: 7cm;
|
||||
width: 16cm;
|
||||
position: relative;
|
||||
margin-bottom: 10px;
|
||||
overflow: hidden;
|
||||
body {
|
||||
background: rgb(204, 204, 204);
|
||||
}
|
||||
.zimbabwe img {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 0;
|
||||
width: 100%;
|
||||
page {
|
||||
background: white;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 0.5cm;
|
||||
box-shadow: 0 0 0.5cm rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
.zimbabwe .qr {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
background: rgb(255, 255, 255, 0.7);
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
line-height: 1.1;
|
||||
page[size='A4'] {
|
||||
width: 21cm;
|
||||
height: 29.7cm;
|
||||
}
|
||||
@media print {
|
||||
body,
|
||||
page {
|
||||
margin: 0;
|
||||
box-shadow: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script src="{{ url_for('static', filename='vendor/vue@2.6.11/vue.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='vendor/vue-router@3.1.6/vue-router.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='vendor/vuex@3.1.3/vuex.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='vendor/quasar@1.10.4/quasar.umd.js') }}"></script>
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="/static/__bundle__/base.js?a52a989e"
|
||||
></script>
|
||||
<script src="{{ url_for('static', filename='vendor/vue-qrcode@1.0.2/vue-qrcode.min.js') }}"></script>
|
||||
<script>
|
||||
Vue.component(VueQrcode.name, VueQrcode)
|
||||
|
||||
new Vue({
|
||||
el: '#vue',
|
||||
created: function () {
|
||||
window.print()
|
||||
mixins: [windowMixin],
|
||||
data: function () {
|
||||
return {
|
||||
theurl: location.host,
|
||||
printDialog: {
|
||||
show: true,
|
||||
data: null
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
@ -4,7 +4,7 @@ from http import HTTPStatus
|
||||
from lnbits.decorators import check_user_exists, validate_uuids
|
||||
|
||||
from lnbits.extensions.withdraw import withdraw_ext
|
||||
from .crud import get_withdraw_link
|
||||
from .crud import get_withdraw_link, chunks
|
||||
|
||||
|
||||
@withdraw_ext.route("/")
|
||||
@ -17,6 +17,7 @@ def index():
|
||||
@withdraw_ext.route("/<link_id>")
|
||||
def display(link_id):
|
||||
link = get_withdraw_link(link_id) or abort(HTTPStatus.NOT_FOUND, "Withdraw link does not exist.")
|
||||
link = get_withdraw_link(link_id, len(link.usescsv.split(","))) or abort(HTTPStatus.NOT_FOUND, "Withdraw link does not exist.")
|
||||
|
||||
return render_template("withdraw/display.html", link=link)
|
||||
|
||||
@ -24,5 +25,12 @@ def display(link_id):
|
||||
@withdraw_ext.route("/print/<link_id>")
|
||||
def print_qr(link_id):
|
||||
link = get_withdraw_link(link_id) or abort(HTTPStatus.NOT_FOUND, "Withdraw link does not exist.")
|
||||
|
||||
return render_template("withdraw/print_qr.html", link=link)
|
||||
if link.uses == 0:
|
||||
return render_template("withdraw/print_qr.html", link=link, unique=False)
|
||||
links = []
|
||||
for x in link.usescsv.split(","):
|
||||
linkk = get_withdraw_link(link_id, x) or abort(HTTPStatus.NOT_FOUND, "Withdraw link does not exist.")
|
||||
links.append(linkk)
|
||||
page_link = list(chunks(links, 4))
|
||||
linked = list(chunks(page_link, 8))
|
||||
return render_template("withdraw/print_qr.html", link=linked, unique=True)
|
||||
|
@ -2,6 +2,7 @@ from datetime import datetime
|
||||
from flask import g, jsonify, request
|
||||
from http import HTTPStatus
|
||||
from lnurl.exceptions import InvalidUrl as LnurlInvalidUrl
|
||||
import shortuuid # type: ignore
|
||||
|
||||
from lnbits.core.crud import get_user
|
||||
from lnbits.core.services import pay_invoice
|
||||
@ -26,10 +27,9 @@ def api_links():
|
||||
|
||||
if "all_wallets" in request.args:
|
||||
wallet_ids = get_user(g.wallet.user).wallet_ids
|
||||
|
||||
try:
|
||||
return (
|
||||
jsonify([{**link._asdict(), **{"lnurl": link.lnurl}} for link in get_withdraw_links(wallet_ids)]),
|
||||
jsonify([{**link._asdict(), **{"lnurl": link.lnurl}} for link in get_withdraw_links(wallet_ids)[0]]),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
except LnurlInvalidUrl:
|
||||
@ -49,7 +49,7 @@ def api_link_retrieve(link_id):
|
||||
|
||||
if link.wallet != g.wallet.id:
|
||||
return jsonify({"message": "Not your withdraw link."}), HTTPStatus.FORBIDDEN
|
||||
|
||||
|
||||
return jsonify({**link._asdict(), **{"lnurl": link.lnurl}}), HTTPStatus.OK
|
||||
|
||||
|
||||
@ -76,6 +76,15 @@ def api_link_create_or_update(link_id=None):
|
||||
if (g.data["max_withdrawable"] * g.data["uses"] * 1000) > g.wallet.balance_msat:
|
||||
return jsonify({"message": "Insufficient balance."}), HTTPStatus.FORBIDDEN
|
||||
|
||||
usescsv = ""
|
||||
|
||||
for i in range(g.data["uses"]):
|
||||
if g.data["is_unique"]:
|
||||
usescsv += "," + str(i + 1)
|
||||
else:
|
||||
usescsv += "," + str(1)
|
||||
usescsv = usescsv[1:]
|
||||
|
||||
if link_id:
|
||||
link = get_withdraw_link(link_id)
|
||||
|
||||
@ -85,9 +94,9 @@ def api_link_create_or_update(link_id=None):
|
||||
if link.wallet != g.wallet.id:
|
||||
return jsonify({"message": "Not your withdraw link."}), HTTPStatus.FORBIDDEN
|
||||
|
||||
link = update_withdraw_link(link_id, **g.data)
|
||||
link = update_withdraw_link(link_id, **g.data, usescsv=usescsv, used=0)
|
||||
else:
|
||||
link = create_withdraw_link(wallet_id=g.wallet.id, **g.data)
|
||||
link = create_withdraw_link(wallet_id=g.wallet.id, **g.data, usescsv=usescsv)
|
||||
|
||||
return jsonify({**link._asdict(), **{"lnurl": link.lnurl}}), HTTPStatus.OK if link_id else HTTPStatus.CREATED
|
||||
|
||||
@ -107,6 +116,7 @@ def api_link_delete(link_id):
|
||||
|
||||
return "", HTTPStatus.NO_CONTENT
|
||||
|
||||
#FOR LNURLs WHICH ARE NOT UNIQUE
|
||||
|
||||
@withdraw_ext.route("/api/v1/lnurl/<unique_hash>", methods=["GET"])
|
||||
def api_lnurl_response(unique_hash):
|
||||
@ -115,10 +125,51 @@ def api_lnurl_response(unique_hash):
|
||||
if not link:
|
||||
return jsonify({"status": "ERROR", "reason": "LNURL-withdraw not found."}), HTTPStatus.OK
|
||||
|
||||
link = update_withdraw_link(link.id, k1=urlsafe_short_hash())
|
||||
if link.is_unique == 1:
|
||||
return jsonify({"status": "ERROR", "reason": "LNURL-withdraw not found."}), HTTPStatus.OK
|
||||
usescsv = ""
|
||||
for x in range(1, link.uses - link.used):
|
||||
usescsv += "," + str(1)
|
||||
usescsv = usescsv[1:]
|
||||
link = update_withdraw_link(link.id, used=link.used + 1, usescsv=usescsv)
|
||||
|
||||
|
||||
return jsonify(link.lnurl_response.dict()), HTTPStatus.OK
|
||||
|
||||
#FOR LNURLs WHICH ARE UNIQUE
|
||||
|
||||
@withdraw_ext.route("/api/v1/lnurl/<unique_hash>/<id_unique_hash>", methods=["GET"])
|
||||
def api_lnurl_multi_response(unique_hash, id_unique_hash):
|
||||
link = get_withdraw_link_by_hash(unique_hash)
|
||||
|
||||
if not link:
|
||||
return jsonify({"status": "ERROR", "reason": "LNURL-withdraw not found."}), HTTPStatus.OK
|
||||
useslist = link.usescsv.split(",")
|
||||
usescsv = ""
|
||||
hashed = []
|
||||
found = False
|
||||
print(link.uses - link.used)
|
||||
print("link.uses - link.used")
|
||||
print("svfsfv")
|
||||
if link.is_unique == 0:
|
||||
for x in range(link.uses - link.used):
|
||||
usescsv += "," + str(1)
|
||||
else:
|
||||
for x in useslist:
|
||||
tohash = link.id + link.unique_hash + str(x)
|
||||
if id_unique_hash == shortuuid.uuid(name=tohash):
|
||||
found = True
|
||||
else:
|
||||
usescsv += "," + x
|
||||
print(x)
|
||||
print("usescsv: " + usescsv)
|
||||
if not found:
|
||||
return jsonify({"status": "ERROR", "reason": "LNURL-withdraw not found."}), HTTPStatus.OK
|
||||
|
||||
usescsv = usescsv[1:]
|
||||
link = update_withdraw_link(link.id, used=link.used + 1, usescsv=usescsv)
|
||||
return jsonify(link.lnurl_response.dict()), HTTPStatus.OK
|
||||
|
||||
|
||||
@withdraw_ext.route("/api/v1/lnurl/cb/<unique_hash>", methods=["GET"])
|
||||
def api_lnurl_callback(unique_hash):
|
||||
@ -143,13 +194,9 @@ def api_lnurl_callback(unique_hash):
|
||||
pay_invoice(wallet_id=link.wallet, bolt11=payment_request, max_sat=link.max_withdrawable)
|
||||
|
||||
changes = {
|
||||
"used": link.used + 1,
|
||||
"open_time": link.wait_time + now,
|
||||
}
|
||||
|
||||
if link.is_unique:
|
||||
changes["unique_hash"] = urlsafe_short_hash()
|
||||
|
||||
update_withdraw_link(link.id, **changes)
|
||||
|
||||
except ValueError as e:
|
||||
|
Loading…
Reference in New Issue
Block a user