Fix update validations (#30)
* fix: no `username` update * fix: update for old pay_links * chore: code format
This commit is contained in:
parent
8bad631fb6
commit
257f5d34d2
9 changed files with 252 additions and 94 deletions
|
|
@ -1,4 +1,5 @@
|
||||||
# LNURLp - <small>[LNbits](https://github.com/lnbits/lnbits) extension</small>
|
# LNURLp - <small>[LNbits](https://github.com/lnbits/lnbits) extension</small>
|
||||||
|
|
||||||
<small>For more about LNBits extension check [this tutorial](https://github.com/lnbits/lnbits/wiki/LNbits-Extensions)</small>
|
<small>For more about LNBits extension check [this tutorial](https://github.com/lnbits/lnbits/wiki/LNbits-Extensions)</small>
|
||||||
|
|
||||||
## Create a static QR code or LNaddress people can use to pay over Lightning Network
|
## Create a static QR code or LNaddress people can use to pay over Lightning Network
|
||||||
|
|
@ -39,7 +40,7 @@ Now that the extensions are taken out of core LNbits we can update each extensio
|
||||||
This new version of the extension will give you the option to add a Lightning Address to each LNURLpay link.
|
This new version of the extension will give you the option to add a Lightning Address to each LNURLpay link.
|
||||||
|
|
||||||
- Open your LNbits instance as super admin (not as a regular user. You will find the SuperUser-ID in your server logs on restart of LNbits. Use that to bookmark and manage LNbits from there in the future.)
|
- Open your LNbits instance as super admin (not as a regular user. You will find the SuperUser-ID in your server logs on restart of LNbits. Use that to bookmark and manage LNbits from there in the future.)
|
||||||
Now lets install the new version of a given extension like extensively [described in this guide](https://github.com/lnbits/lnbits/blob/main/docs/guide/extension-install.md#install-new-extension). In short:
|
Now lets install the new version of a given extension like extensively [described in this guide](https://github.com/lnbits/lnbits/blob/main/docs/guide/extension-install.md#install-new-extension). In short:
|
||||||
- Go to "Mange extensions", click on "ALL", search for e.g. LNURLp, click on "Manage"
|
- Go to "Mange extensions", click on "ALL", search for e.g. LNURLp, click on "Manage"
|
||||||
- Open the details of the extension and click on version 0.2.1, click "Install". You´re done!
|
- Open the details of the extension and click on version 0.2.1, click "Install". You´re done!
|
||||||
|
|
||||||
|
|
@ -55,7 +56,4 @@ Now you can receive sats to your newly created LN address. You will find this in
|
||||||
|
|
||||||
[](https://postimg.cc/3WwsXJHP)
|
[](https://postimg.cc/3WwsXJHP)
|
||||||
|
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,16 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
|
from environs import Env
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from lnbits.db import Database
|
from lnbits.db import Database
|
||||||
from lnbits.helpers import template_renderer
|
from lnbits.helpers import template_renderer
|
||||||
from lnbits.tasks import catch_everything_and_restart
|
from lnbits.tasks import catch_everything_and_restart
|
||||||
from loguru import logger
|
|
||||||
|
|
||||||
|
|
||||||
from .nostr.event import Event
|
from .nostr.event import Event
|
||||||
from .nostr.key import PrivateKey, PublicKey
|
from .nostr.key import PrivateKey, PublicKey
|
||||||
from environs import Env
|
|
||||||
|
|
||||||
|
|
||||||
def generate_keys(private_key: str = ""):
|
def generate_keys(private_key: str = ""):
|
||||||
|
|
|
||||||
3
crud.py
3
crud.py
|
|
@ -1,4 +1,3 @@
|
||||||
import re
|
|
||||||
from typing import List, Optional, Union
|
from typing import List, Optional, Union
|
||||||
|
|
||||||
from lnbits.helpers import urlsafe_short_hash
|
from lnbits.helpers import urlsafe_short_hash
|
||||||
|
|
@ -105,7 +104,7 @@ async def get_pay_links(wallet_ids: Union[str, List[str]]) -> List[PayLink]:
|
||||||
|
|
||||||
|
|
||||||
async def update_pay_link(link_id: str, **kwargs) -> Optional[PayLink]:
|
async def update_pay_link(link_id: str, **kwargs) -> Optional[PayLink]:
|
||||||
if "username" in kwargs and len(kwargs["username"]) > 0:
|
if "username" in kwargs and len(kwargs["username"] or "") > 0:
|
||||||
await check_lnaddress_format(kwargs["username"])
|
await check_lnaddress_format(kwargs["username"])
|
||||||
await check_lnaddress_not_exists(kwargs["username"])
|
await check_lnaddress_not_exists(kwargs["username"])
|
||||||
|
|
||||||
|
|
|
||||||
13
lnurl.py
13
lnurl.py
|
|
@ -3,18 +3,13 @@ from urllib.parse import urlparse
|
||||||
|
|
||||||
from fastapi import Query, Request
|
from fastapi import Query, Request
|
||||||
from lnurl import LnurlErrorResponse, LnurlPayActionResponse, LnurlPayResponse
|
from lnurl import LnurlErrorResponse, LnurlPayActionResponse, LnurlPayResponse
|
||||||
from loguru import logger
|
|
||||||
from starlette.exceptions import HTTPException
|
from starlette.exceptions import HTTPException
|
||||||
|
|
||||||
from lnbits.core.services import create_invoice
|
from lnbits.core.services import create_invoice
|
||||||
from lnbits.utils.exchange_rates import get_fiat_rate_satoshis
|
from lnbits.utils.exchange_rates import get_fiat_rate_satoshis
|
||||||
|
|
||||||
from . import lnurlp_ext
|
from . import lnurlp_ext, nostr_publickey
|
||||||
from .crud import increment_pay_link, get_pay_link, get_address_data
|
from .crud import increment_pay_link
|
||||||
from loguru import logger
|
|
||||||
from urllib.parse import urlparse
|
|
||||||
import json
|
|
||||||
from . import nostr_publickey
|
|
||||||
|
|
||||||
|
|
||||||
@lnurlp_ext.get(
|
@lnurlp_ext.get(
|
||||||
|
|
@ -132,7 +127,9 @@ async def api_lnurl_response(request: Request, link_id, lnaddress=False):
|
||||||
if lnaddress:
|
if lnaddress:
|
||||||
# for lnaddress, we have to set this otherwise the metadata won't have the identifier
|
# for lnaddress, we have to set this otherwise the metadata won't have the identifier
|
||||||
link.domain = urlparse(str(request.url)).netloc
|
link.domain = urlparse(str(request.url)).netloc
|
||||||
callback = str(request.url_for("lnurlp.api_lnurl_lnaddr_callback", link_id=link.id))
|
callback = str(
|
||||||
|
request.url_for("lnurlp.api_lnurl_lnaddr_callback", link_id=link.id)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
callback = str(request.url_for("lnurlp.api_lnurl_callback", link_id=link.id))
|
callback = str(request.url_for("lnurlp.api_lnurl_callback", link_id=link.id))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ class CreatePayLinkData(BaseModel):
|
||||||
success_url: str = Query(None)
|
success_url: str = Query(None)
|
||||||
fiat_base_multiplier: int = Query(100, ge=1)
|
fiat_base_multiplier: int = Query(100, ge=1)
|
||||||
username: str = Query(None)
|
username: str = Query(None)
|
||||||
zaps: bool = Query(False)
|
zaps: Optional[bool] = Query(False)
|
||||||
|
|
||||||
|
|
||||||
class PayLink(BaseModel):
|
class PayLink(BaseModel):
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ new Vue({
|
||||||
show: false,
|
show: false,
|
||||||
fixedAmount: true,
|
fixedAmount: true,
|
||||||
data: {
|
data: {
|
||||||
zaps:false
|
zaps: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
qrCodeDialog: {
|
qrCodeDialog: {
|
||||||
|
|
|
||||||
14
tasks.py
14
tasks.py
|
|
@ -1,23 +1,21 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
|
import time
|
||||||
|
from threading import Thread
|
||||||
|
from typing import List
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
from websocket import WebSocketApp
|
||||||
|
|
||||||
from lnbits.core.crud import update_payment_extra
|
from lnbits.core.crud import update_payment_extra
|
||||||
from lnbits.core.models import Payment
|
from lnbits.core.models import Payment
|
||||||
from lnbits.helpers import get_current_extension_name
|
from lnbits.helpers import get_current_extension_name
|
||||||
from lnbits.tasks import register_invoice_listener
|
from lnbits.tasks import register_invoice_listener
|
||||||
from websocket import WebSocketApp
|
|
||||||
from lnbits.settings import settings
|
|
||||||
from .crud import get_pay_link
|
|
||||||
from threading import Thread
|
|
||||||
from . import nostr_privatekey
|
|
||||||
from typing import List
|
|
||||||
import time
|
|
||||||
|
|
||||||
|
from . import nostr_privatekey
|
||||||
|
from .crud import get_pay_link
|
||||||
from .nostr.event import Event
|
from .nostr.event import Event
|
||||||
from .nostr.key import PrivateKey, PublicKey
|
|
||||||
|
|
||||||
|
|
||||||
async def wait_for_paid_invoices():
|
async def wait_for_paid_invoices():
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@
|
||||||
<div class="col-12 col-md-7 q-gutter-y-md">
|
<div class="col-12 col-md-7 q-gutter-y-md">
|
||||||
<q-card>
|
<q-card>
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<q-btn unelevated color="primary" @click="formDialog.show = true">New pay link</q-btn>
|
<q-btn unelevated color="primary" @click="formDialog.show = true"
|
||||||
|
>New pay link</q-btn
|
||||||
|
>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
|
|
||||||
|
|
@ -15,7 +17,13 @@
|
||||||
<h5 class="text-subtitle1 q-my-none">Pay links</h5>
|
<h5 class="text-subtitle1 q-my-none">Pay links</h5>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<q-table dense flat :data="payLinks" row-key="id" :pagination.sync="payLinksTable.pagination">
|
<q-table
|
||||||
|
dense
|
||||||
|
flat
|
||||||
|
:data="payLinks"
|
||||||
|
row-key="id"
|
||||||
|
:pagination.sync="payLinksTable.pagination"
|
||||||
|
>
|
||||||
{% raw %}
|
{% raw %}
|
||||||
<template v-slot:header="props">
|
<template v-slot:header="props">
|
||||||
<q-tr class="text-left" :props="props">
|
<q-tr class="text-left" :props="props">
|
||||||
|
|
@ -31,10 +39,26 @@
|
||||||
<template v-slot:body="props">
|
<template v-slot:body="props">
|
||||||
<q-tr :props="props">
|
<q-tr :props="props">
|
||||||
<q-td auto-width>
|
<q-td auto-width>
|
||||||
<q-btn unelevated dense size="xs" icon="launch" :color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
|
<q-btn
|
||||||
type="a" :href="props.row.pay_url" target="_blank"><q-tooltip>Shareable Page</q-tooltip></q-btn>
|
unelevated
|
||||||
<q-btn unelevated dense size="xs" icon="visibility" :color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
|
dense
|
||||||
@click="openQrCodeDialog(props.row.id)"><q-tooltip>View Link</q-tooltip></q-btn>
|
size="xs"
|
||||||
|
icon="launch"
|
||||||
|
:color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
|
||||||
|
type="a"
|
||||||
|
:href="props.row.pay_url"
|
||||||
|
target="_blank"
|
||||||
|
><q-tooltip>Shareable Page</q-tooltip></q-btn
|
||||||
|
>
|
||||||
|
<q-btn
|
||||||
|
unelevated
|
||||||
|
dense
|
||||||
|
size="xs"
|
||||||
|
icon="visibility"
|
||||||
|
:color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
|
||||||
|
@click="openQrCodeDialog(props.row.id)"
|
||||||
|
><q-tooltip>View Link</q-tooltip></q-btn
|
||||||
|
>
|
||||||
</q-td>
|
</q-td>
|
||||||
<q-td auto-width>{{ props.row.description }}</q-td>
|
<q-td auto-width>{{ props.row.description }}</q-td>
|
||||||
<q-td auto-width>
|
<q-td auto-width>
|
||||||
|
|
@ -44,30 +68,57 @@
|
||||||
<span v-else>{{ props.row.min }} - {{ props.row.max }}</span>
|
<span v-else>{{ props.row.min }} - {{ props.row.max }}</span>
|
||||||
</q-td>
|
</q-td>
|
||||||
<q-td>{{ props.row.currency || 'sat' }}</q-td>
|
<q-td>{{ props.row.currency || 'sat' }}</q-td>
|
||||||
<q-td auto-width :class="(props.row.username) ? 'text-normal' : 'text-grey'">{{ props.row.username ||
|
<q-td
|
||||||
'None' }}</q-td>
|
auto-width
|
||||||
|
:class="(props.row.username) ? 'text-normal' : 'text-grey'"
|
||||||
|
>{{ props.row.username || 'None' }}</q-td
|
||||||
|
>
|
||||||
<q-td>
|
<q-td>
|
||||||
<q-icon v-if="props.row.webhook_url" size="14px" name="http">
|
<q-icon v-if="props.row.webhook_url" size="14px" name="http">
|
||||||
<q-tooltip>Webhook to {{ props.row.webhook_url }}</q-tooltip>
|
<q-tooltip>Webhook to {{ props.row.webhook_url }}</q-tooltip>
|
||||||
</q-icon>
|
</q-icon>
|
||||||
<q-icon v-if="props.row.success_text || props.row.success_url" size="14px" name="call_to_action">
|
<q-icon
|
||||||
|
v-if="props.row.success_text || props.row.success_url"
|
||||||
|
size="14px"
|
||||||
|
name="call_to_action"
|
||||||
|
>
|
||||||
<q-tooltip>
|
<q-tooltip>
|
||||||
On success, show message '{{ props.row.success_text }}'
|
On success, show message '{{ props.row.success_text }}'
|
||||||
<span v-if="props.row.success_url">and URL '{{ props.row.success_url }}'</span>
|
<span v-if="props.row.success_url"
|
||||||
|
>and URL '{{ props.row.success_url }}'</span
|
||||||
|
>
|
||||||
</q-tooltip>
|
</q-tooltip>
|
||||||
</q-icon>
|
</q-icon>
|
||||||
<q-icon v-if="props.row.comment_chars > 0" size="14px" name="insert_comment">
|
<q-icon
|
||||||
|
v-if="props.row.comment_chars > 0"
|
||||||
|
size="14px"
|
||||||
|
name="insert_comment"
|
||||||
|
>
|
||||||
<q-tooltip>
|
<q-tooltip>
|
||||||
{{ props.row.comment_chars }}-char comment allowed
|
{{ props.row.comment_chars }}-char comment allowed
|
||||||
</q-tooltip>
|
</q-tooltip>
|
||||||
</q-icon>
|
</q-icon>
|
||||||
</q-td>
|
</q-td>
|
||||||
<q-td auto-width>
|
<q-td auto-width>
|
||||||
<q-btn flat dense size="xs" @click="openUpdateDialog(props.row.id)" icon="edit" color="light-blue">
|
<q-btn
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
size="xs"
|
||||||
|
@click="openUpdateDialog(props.row.id)"
|
||||||
|
icon="edit"
|
||||||
|
color="light-blue"
|
||||||
|
>
|
||||||
<q-tooltip>Edit</q-tooltip>
|
<q-tooltip>Edit</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
<q-btn flat dense size="xs" @click="deletePayLink(props.row.id)" icon="cancel"
|
<q-btn
|
||||||
color="pink"><q-tooltip>Delete</q-tooltip></q-btn>
|
flat
|
||||||
|
dense
|
||||||
|
size="xs"
|
||||||
|
@click="deletePayLink(props.row.id)"
|
||||||
|
icon="cancel"
|
||||||
|
color="pink"
|
||||||
|
><q-tooltip>Delete</q-tooltip></q-btn
|
||||||
|
>
|
||||||
</q-td>
|
</q-td>
|
||||||
</q-tr>
|
</q-tr>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -98,14 +149,33 @@
|
||||||
<q-dialog v-model="formDialog.show" @hide="closeFormDialog">
|
<q-dialog v-model="formDialog.show" @hide="closeFormDialog">
|
||||||
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||||
<q-form @submit="sendFormData" class="q-gutter-md">
|
<q-form @submit="sendFormData" class="q-gutter-md">
|
||||||
<q-select filled dense emit-value v-model="formDialog.data.wallet" :options="g.user.walletOptions"
|
<q-select
|
||||||
label="Wallet *">
|
filled
|
||||||
|
dense
|
||||||
|
emit-value
|
||||||
|
v-model="formDialog.data.wallet"
|
||||||
|
:options="g.user.walletOptions"
|
||||||
|
label="Wallet *"
|
||||||
|
>
|
||||||
</q-select>
|
</q-select>
|
||||||
<q-input filled dense v-model.trim="formDialog.data.description" type="text" label="Item description *">
|
<q-input
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model.trim="formDialog.data.description"
|
||||||
|
type="text"
|
||||||
|
label="Item description *"
|
||||||
|
>
|
||||||
</q-input>
|
</q-input>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<q-input filled dense v-model.trim="formDialog.data.username" type="text" label="Lightning Address" @input="formDialog.data.username = formDialog.data.username.toLowerCase()" />
|
<q-input
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model.trim="formDialog.data.username"
|
||||||
|
type="text"
|
||||||
|
label="Lightning Address"
|
||||||
|
@input="formDialog.data.username = formDialog.data.username.toLowerCase()"
|
||||||
|
/>
|
||||||
<span class="label">
|
<span class="label">
|
||||||
@ {% raw %} {{ domain }} {% endraw %}
|
@ {% raw %} {{ domain }} {% endraw %}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -117,64 +187,122 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row q-col-gutter-sm q-mx-sm">
|
<div class="row q-col-gutter-sm q-mx-sm">
|
||||||
<q-input filled dense v-model.number="formDialog.data.min" type="number"
|
<q-input
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model.number="formDialog.data.min"
|
||||||
|
type="number"
|
||||||
:step="formDialog.data.currency && formDialog.data.currency !== 'satoshis' ? '0.01' : '1'"
|
:step="formDialog.data.currency && formDialog.data.currency !== 'satoshis' ? '0.01' : '1'"
|
||||||
:label="formDialog.fixedAmount ? 'Amount *' : 'Min *'"></q-input>
|
:label="formDialog.fixedAmount ? 'Amount *' : 'Min *'"
|
||||||
<q-input v-if="!formDialog.fixedAmount" filled dense v-model.number="formDialog.data.max" type="number"
|
></q-input>
|
||||||
:step="formDialog.data.currency && formDialog.data.currency !== 'satoshis' ? '0.01' : '1'" label="Max *">
|
<q-input
|
||||||
|
v-if="!formDialog.fixedAmount"
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model.number="formDialog.data.max"
|
||||||
|
type="number"
|
||||||
|
:step="formDialog.data.currency && formDialog.data.currency !== 'satoshis' ? '0.01' : '1'"
|
||||||
|
label="Max *"
|
||||||
|
>
|
||||||
</q-input>
|
</q-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="row q-col-gutter-sm">
|
<div class="row q-col-gutter-sm">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<q-checkbox dense v-model="formDialog.fixedAmount" label="Fixed amount" />
|
<q-checkbox
|
||||||
|
dense
|
||||||
|
v-model="formDialog.fixedAmount"
|
||||||
|
label="Fixed amount"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<q-select dense :options="currencies" v-model="formDialog.data.currency"
|
<q-select
|
||||||
:display-value="formDialog.data.currency || 'satoshis'" label="Currency"
|
dense
|
||||||
|
:options="currencies"
|
||||||
|
v-model="formDialog.data.currency"
|
||||||
|
:display-value="formDialog.data.currency || 'satoshis'"
|
||||||
|
label="Currency"
|
||||||
:hint="'Converted to satoshis at each payment. ' + (formDialog.data.currency && fiatRates[formDialog.data.currency] ? `Currently 1 ${formDialog.data.currency} = ${fiatRates[formDialog.data.currency]} sat` : '')"
|
:hint="'Converted to satoshis at each payment. ' + (formDialog.data.currency && fiatRates[formDialog.data.currency] ? `Currently 1 ${formDialog.data.currency} = ${fiatRates[formDialog.data.currency]} sat` : '')"
|
||||||
@input="updateFiatRate" />
|
@input="updateFiatRate"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<q-expansion-item group="advanced" icon="settings" label="Advanced options">
|
<q-expansion-item
|
||||||
|
group="advanced"
|
||||||
|
icon="settings"
|
||||||
|
label="Advanced options"
|
||||||
|
>
|
||||||
<q-card>
|
<q-card>
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">LNURL</h5>
|
<h5 class="text-caption q-mt-sm q-mb-none">LNURL</h5>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<q-input filled dense v-model.number="formDialog.data.comment_chars" type="number"
|
<q-input
|
||||||
label="Comment maximum characters" hint="Allow the payer to attach a comment.">
|
filled
|
||||||
|
dense
|
||||||
|
v-model.number="formDialog.data.comment_chars"
|
||||||
|
type="number"
|
||||||
|
label="Comment maximum characters"
|
||||||
|
hint="Allow the payer to attach a comment."
|
||||||
|
>
|
||||||
</q-input>
|
</q-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<q-input filled dense v-model="formDialog.data.webhook_url" type="text" label="Webhook URL (optional)"
|
<q-input
|
||||||
hint="A URL to be called whenever this link receives a payment."></q-input>
|
filled
|
||||||
|
dense
|
||||||
|
v-model="formDialog.data.webhook_url"
|
||||||
|
type="text"
|
||||||
|
label="Webhook URL (optional)"
|
||||||
|
hint="A URL to be called whenever this link receives a payment."
|
||||||
|
></q-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row" v-if="formDialog.data.webhook_url">
|
<div class="row" v-if="formDialog.data.webhook_url">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<q-input filled dense v-model="formDialog.data.webhook_headers" type="text"
|
<q-input
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model="formDialog.data.webhook_headers"
|
||||||
|
type="text"
|
||||||
label="Webhook headers (optional)"
|
label="Webhook headers (optional)"
|
||||||
hint="Custom data as JSON string, send headers along with the webhook."></q-input>
|
hint="Custom data as JSON string, send headers along with the webhook."
|
||||||
|
></q-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<q-input filled dense v-model="formDialog.data.webhook_body" type="text"
|
<q-input
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model="formDialog.data.webhook_body"
|
||||||
|
type="text"
|
||||||
label="Webhook custom data (optional)"
|
label="Webhook custom data (optional)"
|
||||||
hint="Custom data as JSON string, will get posted along with webhook 'body' field."></q-input>
|
hint="Custom data as JSON string, will get posted along with webhook 'body' field."
|
||||||
|
></q-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<q-input filled dense v-model="formDialog.data.success_text" type="text"
|
<q-input
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model="formDialog.data.success_text"
|
||||||
|
type="text"
|
||||||
label="Success message (optional)"
|
label="Success message (optional)"
|
||||||
hint="Will be shown to the user in his wallet after a successful payment."></q-input>
|
hint="Will be shown to the user in his wallet after a successful payment."
|
||||||
|
></q-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<q-input filled dense v-model="formDialog.data.success_url" type="text" label="Success URL (optional)"
|
<q-input
|
||||||
hint="Link will be shown to the sender after a successful payment.">
|
filled
|
||||||
|
dense
|
||||||
|
v-model="formDialog.data.success_url"
|
||||||
|
type="text"
|
||||||
|
label="Success URL (optional)"
|
||||||
|
hint="Link will be shown to the sender after a successful payment."
|
||||||
|
>
|
||||||
</q-input>
|
</q-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -183,24 +311,43 @@
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Nostr</h5>
|
<h5 class="text-caption q-mt-sm q-mb-none">Nostr</h5>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<q-checkbox :toggle-indeterminate="false" dense v-model="formDialog.data.zaps"
|
<q-checkbox
|
||||||
label="Enable nostr zaps" />
|
:toggle-indeterminate="false"
|
||||||
|
dense
|
||||||
|
v-model="formDialog.data.zaps"
|
||||||
|
label="Enable nostr zaps"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-expansion-item>
|
</q-expansion-item>
|
||||||
<div class="row q-mt-lg">
|
<div class="row q-mt-lg">
|
||||||
<q-btn v-if="formDialog.data.id" unelevated color="primary" type="submit">Update pay link</q-btn>
|
<q-btn
|
||||||
<q-btn v-else unelevated color="primary" :disable="
|
v-if="formDialog.data.id"
|
||||||
|
unelevated
|
||||||
|
color="primary"
|
||||||
|
type="submit"
|
||||||
|
>Update pay link</q-btn
|
||||||
|
>
|
||||||
|
<q-btn
|
||||||
|
v-else
|
||||||
|
unelevated
|
||||||
|
color="primary"
|
||||||
|
:disable="
|
||||||
formDialog.data.wallet == null ||
|
formDialog.data.wallet == null ||
|
||||||
formDialog.data.description == null ||
|
formDialog.data.description == null ||
|
||||||
(
|
(
|
||||||
formDialog.data.min == null ||
|
formDialog.data.min == null ||
|
||||||
formDialog.data.min <= 0
|
formDialog.data.min <= 0
|
||||||
)
|
)
|
||||||
" type="submit">Create pay link</q-btn>
|
"
|
||||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
|
type="submit"
|
||||||
|
>Create pay link</q-btn
|
||||||
|
>
|
||||||
|
<q-btn v-close-popup flat color="grey" class="q-ml-auto"
|
||||||
|
>Cancel</q-btn
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</q-form>
|
</q-form>
|
||||||
</q-card>
|
</q-card>
|
||||||
|
|
@ -210,20 +357,22 @@
|
||||||
<q-card v-if="qrCodeDialog.data" class="q-pa-lg lnbits__dialog-card">
|
<q-card v-if="qrCodeDialog.data" class="q-pa-lg lnbits__dialog-card">
|
||||||
{% raw %}
|
{% raw %}
|
||||||
<q-responsive :ratio="1" class="q-mx-xl q-mb-md">
|
<q-responsive :ratio="1" class="q-mx-xl q-mb-md">
|
||||||
<qrcode :value="'lightning:' + qrCodeDialog.data.lnurl" :options="{width: 800}" class="rounded-borders">
|
<qrcode
|
||||||
|
:value="'lightning:' + qrCodeDialog.data.lnurl"
|
||||||
|
:options="{width: 800}"
|
||||||
|
class="rounded-borders"
|
||||||
|
>
|
||||||
</qrcode>
|
</qrcode>
|
||||||
</q-responsive>
|
</q-responsive>
|
||||||
<p style="word-break: break-all">
|
<p style="word-break: break-all">
|
||||||
<strong>ID:</strong> {{ qrCodeDialog.data.id }}<br />
|
<strong>ID:</strong> {{ qrCodeDialog.data.id }}<br />
|
||||||
<strong>Amount:</strong> {{ qrCodeDialog.data.amount }}<br />
|
<strong>Amount:</strong> {{ qrCodeDialog.data.amount }}<br />
|
||||||
<span v-if="qrCodeDialog.data.currency"><strong>{{ qrCodeDialog.data.currency }} price:</strong>
|
<span v-if="qrCodeDialog.data.currency"
|
||||||
{{
|
><strong>{{ qrCodeDialog.data.currency }} price:</strong> {{
|
||||||
fiatRates[qrCodeDialog.data.currency]
|
fiatRates[qrCodeDialog.data.currency] ?
|
||||||
? fiatRates[qrCodeDialog.data.currency] + ' sat'
|
fiatRates[qrCodeDialog.data.currency] + ' sat' : 'Loading...' }}<br
|
||||||
: 'Loading...'
|
/></span>
|
||||||
}}<br /></span>
|
<strong>Accepts comments:</strong> {{ qrCodeDialog.data.comments }}<br />
|
||||||
<strong>Accepts comments:</strong> {{ qrCodeDialog.data.comments
|
|
||||||
}}<br />
|
|
||||||
<strong>Dispatches webhook to:</strong> {{ qrCodeDialog.data.webhook
|
<strong>Dispatches webhook to:</strong> {{ qrCodeDialog.data.webhook
|
||||||
}}<br />
|
}}<br />
|
||||||
<strong>On success:</strong> {{ qrCodeDialog.data.success }}<br />
|
<strong>On success:</strong> {{ qrCodeDialog.data.success }}<br />
|
||||||
|
|
@ -235,17 +384,37 @@
|
||||||
</p>
|
</p>
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
<div class="row q-mt-lg q-gutter-sm">
|
<div class="row q-mt-lg q-gutter-sm">
|
||||||
<q-btn outline color="grey" @click="copyText(qrCodeDialog.data.lnurl, 'LNURL copied to clipboard!')"
|
<q-btn
|
||||||
class="q-ml-sm">Copy LNURL</q-btn>
|
outline
|
||||||
<q-btn outline color="grey" icon="link"
|
color="grey"
|
||||||
@click="copyText(qrCodeDialog.data.pay_url, 'Link copied to clipboard!')"><q-tooltip>Copy sharable
|
@click="copyText(qrCodeDialog.data.lnurl, 'LNURL copied to clipboard!')"
|
||||||
link</q-tooltip>
|
class="q-ml-sm"
|
||||||
|
>Copy LNURL</q-btn
|
||||||
|
>
|
||||||
|
<q-btn
|
||||||
|
outline
|
||||||
|
color="grey"
|
||||||
|
icon="link"
|
||||||
|
@click="copyText(qrCodeDialog.data.pay_url, 'Link copied to clipboard!')"
|
||||||
|
><q-tooltip>Copy sharable link</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
<q-btn outline color="grey" icon="nfc" @click="writeNfcTag(qrCodeDialog.data.lnurl)"
|
<q-btn
|
||||||
:disable="nfcTagWriting"><q-tooltip>Write to NFC</q-tooltip>
|
outline
|
||||||
|
color="grey"
|
||||||
|
icon="nfc"
|
||||||
|
@click="writeNfcTag(qrCodeDialog.data.lnurl)"
|
||||||
|
:disable="nfcTagWriting"
|
||||||
|
><q-tooltip>Write to NFC</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
<q-btn outline color="grey" icon="print" type="a" :href="qrCodeDialog.data.print_url"
|
<q-btn
|
||||||
target="_blank"><q-tooltip>Print</q-tooltip></q-btn>
|
outline
|
||||||
|
color="grey"
|
||||||
|
icon="print"
|
||||||
|
type="a"
|
||||||
|
:href="qrCodeDialog.data.print_url"
|
||||||
|
target="_blank"
|
||||||
|
><q-tooltip>Print</q-tooltip></q-btn
|
||||||
|
>
|
||||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Close</q-btn>
|
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Close</q-btn>
|
||||||
</div>
|
</div>
|
||||||
</q-card>
|
</q-card>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import json
|
import json
|
||||||
from asyncio.log import logger
|
from asyncio.log import logger
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
from urllib.parse import urlparse
|
|
||||||
|
|
||||||
from fastapi import Depends, Query, Request
|
from fastapi import Depends, Query, Request
|
||||||
from lnurl.exceptions import InvalidUrl as LnurlInvalidUrl
|
from lnurl.exceptions import InvalidUrl as LnurlInvalidUrl
|
||||||
|
|
@ -89,7 +88,6 @@ async def api_link_create_or_update(
|
||||||
link_id=None,
|
link_id=None,
|
||||||
wallet: WalletTypeInfo = Depends(get_key_type),
|
wallet: WalletTypeInfo = Depends(get_key_type),
|
||||||
):
|
):
|
||||||
|
|
||||||
if data.min > data.max:
|
if data.min > data.max:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
detail="Min is greater than max.", status_code=HTTPStatus.BAD_REQUEST
|
detail="Min is greater than max.", status_code=HTTPStatus.BAD_REQUEST
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue