diff --git a/config.json b/config.json index 2e0a5c7..de2cba5 100644 --- a/config.json +++ b/config.json @@ -2,7 +2,7 @@ "name": "Withdraw Links", "short_description": "Make LNURL withdraw links", "tile": "/withdraw/static/image/lnurl-withdraw.png", - "version": "1.1.0", + "version": "1.2.2", "min_lnbits_version": "1.3.0", "contributors": [ { diff --git a/crud.py b/crud.py index b63bd88..b914ae5 100644 --- a/crud.py +++ b/crud.py @@ -4,7 +4,7 @@ import shortuuid from lnbits.db import Database from lnbits.helpers import urlsafe_short_hash -from .models import CreateWithdrawData, HashCheck, WithdrawLink +from .models import CreateWithdrawData, HashCheck, PaginatedWithdraws, WithdrawLink db = Database("ext_withdraw") @@ -66,7 +66,7 @@ async def get_withdraw_link_by_hash(unique_hash: str, num=0) -> WithdrawLink | N async def get_withdraw_links( wallet_ids: list[str], limit: int, offset: int -) -> tuple[list[WithdrawLink], int]: +) -> PaginatedWithdraws: q = ",".join([f"'{w}'" for w in wallet_ids]) query_str = f""" @@ -85,16 +85,15 @@ async def get_withdraw_links( query_params, WithdrawLink, ) - result = await db.execute( f""" SELECT COUNT(*) as total FROM withdraw.withdraw_link WHERE wallet IN ({q}) """ ) - total = result.mappings().first() + result2 = result.mappings().first() - return links, total.total + return PaginatedWithdraws(data=links, total=int(result2.total)) async def remove_unique_withdraw_link(link: WithdrawLink, unique_hash: str) -> None: @@ -109,7 +108,7 @@ async def remove_unique_withdraw_link(link: WithdrawLink, unique_hash: str) -> N async def increment_withdraw_link(link: WithdrawLink) -> None: link.used = link.used + 1 - link.open_time = int(datetime.now().timestamp()) + link.wait_time + link.open_time = int(datetime.now().timestamp()) await update_withdraw_link(link) diff --git a/migrations.py b/migrations.py index 754a57f..e27af8a 100644 --- a/migrations.py +++ b/migrations.py @@ -139,3 +139,9 @@ async def m007_add_created_at_timestamp(db): "ALTER TABLE withdraw.withdraw_link " f"ADD COLUMN created_at TIMESTAMP DEFAULT {db.timestamp_column_default}" ) + + +async def m008_add_enabled_column(db): + await db.execute( + "ALTER TABLE withdraw.withdraw_link ADD COLUMN enabled BOOLEAN DEFAULT true;" + ) diff --git a/models.py b/models.py index e709bd4..e888cdf 100644 --- a/models.py +++ b/models.py @@ -15,6 +15,7 @@ class CreateWithdrawData(BaseModel): webhook_headers: str = Query(None) webhook_body: str = Query(None) custom_url: str = Query(None) + enabled: bool = Query(True) class WithdrawLink(BaseModel): @@ -37,6 +38,22 @@ class WithdrawLink(BaseModel): webhook_body: str = Query(None) custom_url: str = Query(None) created_at: datetime + enabled: bool = Query(True) + lnurl: str | None = Field( + default=None, + no_database=True, + deprecated=True, + description=( + "Deprecated: Instead of using this bech32 encoded string, dynamically " + "generate your own static link (lud17/bech32) on the client side. " + "Example: lnurlw://${window.location.hostname}/lnurlw/${id}" + ), + ) + lnurl_url: str | None = Field( + default=None, + no_database=True, + description="The raw LNURL callback URL (use for QR code generation)", + ) @property def is_spent(self) -> bool: @@ -46,3 +63,8 @@ class WithdrawLink(BaseModel): class HashCheck(BaseModel): hash: bool lnurl: bool + + +class PaginatedWithdraws(BaseModel): + data: list[WithdrawLink] + total: int diff --git a/pyproject.toml b/pyproject.toml index 8f281dd..a091367 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,6 +7,9 @@ authors = [{ name = "Alan Bits", email = "alan@lnbits.com" }] urls = { Homepage = "https://lnbits.com", Repository = "https://github.com/lnbits/bitcoinswitch_extension" } dependencies = [ "lnbits>1" ] +[tool.poetry] +package-mode = false + [tool.uv] dev-dependencies = [ "black", diff --git a/static/js/index.js b/static/js/index.js index e6c6146..0b42b40 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -1,17 +1,6 @@ -const locationPath = [ - window.location.protocol, - '//', - window.location.host, - window.location.pathname -].join('') - const mapWithdrawLink = function (obj) { obj._data = _.clone(obj) - 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('') obj._data.use_custom = Boolean(obj.custom_url) return obj } @@ -25,6 +14,7 @@ window.app = Vue.createApp({ return { checker: null, withdrawLinks: [], + lnurl: '', withdrawLinksTable: { columns: [ {name: 'title', align: 'left', label: 'Title', field: 'title'}, @@ -34,7 +24,7 @@ window.app = Vue.createApp({ label: 'Created At', field: 'created_at', sortable: true, - format: function (val, row) { + format: function (val) { return new Date(val).toLocaleString() } }, @@ -47,7 +37,7 @@ window.app = Vue.createApp({ { name: 'uses', align: 'right', - label: 'Created', + label: 'Uses', field: 'uses' }, { @@ -56,8 +46,13 @@ window.app = Vue.createApp({ 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'} + { + name: 'max_withdrawable', + align: 'right', + label: 'Max (sat)', + field: 'max_withdrawable', + format: LNbits.utils.formatSat + } ], pagination: { page: 1, @@ -73,7 +68,8 @@ window.app = Vue.createApp({ data: { is_unique: false, use_custom: false, - has_webhook: false + has_webhook: false, + enabled: true } }, simpleformDialog: { @@ -83,7 +79,8 @@ window.app = Vue.createApp({ use_custom: false, title: 'Vouchers', min_withdrawable: 0, - wait_time: 1 + wait_time: 1, + enabled: true } }, qrCodeDialog: { @@ -130,22 +127,22 @@ window.app = Vue.createApp({ this.formDialog.data = { is_unique: false, use_custom: false, - has_webhook: false + has_webhook: false, + enabled: true } }, simplecloseFormDialog() { this.simpleformDialog.data = { is_unique: false, - use_custom: false + use_custom: false, + enabled: true } }, openQrCodeDialog(linkId) { const link = _.findWhere(this.withdrawLinks, {id: linkId}) - this.qrCodeDialog.data = _.clone(link) - this.qrCodeDialog.data.url = - window.location.protocol + '//' + window.location.host this.qrCodeDialog.show = true + this.activeUrl = link.lnurl_url }, openUpdateDialog(linkId) { let link = _.findWhere(this.withdrawLinks, {id: linkId}) @@ -258,7 +255,7 @@ window.app = Vue.createApp({ '/withdraw/api/v1/links/' + linkId, _.findWhere(this.g.user.wallets, {id: link.wallet}).adminkey ) - .then(response => { + .then(() => { this.withdrawLinks = _.reject(this.withdrawLinks, function (obj) { return obj.id === linkId }) diff --git a/templates/withdraw/csv.html b/templates/withdraw/csv.html deleted file mode 100644 index d393061..0000000 --- a/templates/withdraw/csv.html +++ /dev/null @@ -1,12 +0,0 @@ -{% extends "print.html" %} {% block page %} {% for page in link %} {% for threes -in page %} {% for one in threes %} {{one}}, {% endfor %} {% endfor %} {% endfor -%} {% endblock %} {% block scripts %} - -{% endblock %} diff --git a/templates/withdraw/display.html b/templates/withdraw/display.html index 34ae1ac..812c95f 100644 --- a/templates/withdraw/display.html +++ b/templates/withdraw/display.html @@ -4,24 +4,32 @@
- {% if link.is_spent %} - Withdraw is spent. - {% endif %} - - + Withdraw is spent. + Withdraw is spent. + Withdraw is disabled. + +
- Copy LNURL
@@ -52,8 +60,11 @@ mixins: [window.windowMixin], data() { return { - here: location.protocol + '//' + location.host, - nfcTagWriting: false + spent: {{ 'true' if spent else 'false' }}, + url: '{{ lnurl_url }}', + lnurl: '', + nfcTagWriting: false, + enabled: {{ 'true' if enabled else 'false' }} } } }) diff --git a/templates/withdraw/index.html b/templates/withdraw/index.html index 1014a2c..f208ece 100644 --- a/templates/withdraw/index.html +++ b/templates/withdraw/index.html @@ -38,6 +38,7 @@ >