feat: add optional domain field (#120)
* feat: add optional domain field closes #119
This commit is contained in:
parent
76c5841bc8
commit
17135b45ae
8 changed files with 107 additions and 46 deletions
1
crud.py
1
crud.py
|
|
@ -65,6 +65,7 @@ async def create_pay_link(data: CreatePayLinkData) -> PayLink:
|
||||||
created_at=now,
|
created_at=now,
|
||||||
updated_at=now,
|
updated_at=now,
|
||||||
disposable=data.disposable if data.disposable is not None else True,
|
disposable=data.disposable if data.disposable is not None else True,
|
||||||
|
domain=data.domain,
|
||||||
)
|
)
|
||||||
|
|
||||||
await db.insert("lnurlp.pay_links", link)
|
await db.insert("lnurlp.pay_links", link)
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,16 @@ def parse_nostr_private_key(key: str) -> PrivateKey:
|
||||||
return PrivateKey(bytes.fromhex(key))
|
return PrivateKey(bytes.fromhex(key))
|
||||||
|
|
||||||
|
|
||||||
def lnurl_encode_link_id(req: Request, link_id: str) -> str:
|
def lnurl_encode_link(req: Request, link_id: str, domain: str | None = None) -> str:
|
||||||
|
if domain:
|
||||||
|
url_str = f"https://{domain}/lnurlp/{link_id}"
|
||||||
|
return str(lnurl_encode(url_str).bech32)
|
||||||
|
|
||||||
url = req.url_for("lnurlp.api_lnurl_response", link_id=link_id)
|
url = req.url_for("lnurlp.api_lnurl_response", link_id=link_id)
|
||||||
url = url.replace(path=url.path)
|
url = url.replace(path=url.path)
|
||||||
url_str = str(url)
|
url_str = str(url)
|
||||||
if url.netloc.endswith(".onion"):
|
if url.netloc.endswith(".onion"):
|
||||||
# change url string scheme to http
|
# change url string scheme to http
|
||||||
url_str = url_str.replace("https://", "http://")
|
url_str = url_str.replace("https://", "http://")
|
||||||
|
|
||||||
return str(lnurl_encode(url_str).bech32)
|
return str(lnurl_encode(url_str).bech32)
|
||||||
|
|
|
||||||
24
models.py
24
models.py
|
|
@ -35,6 +35,7 @@ class CreatePayLinkData(BaseModel):
|
||||||
username: str | None = Query(None)
|
username: str | None = Query(None)
|
||||||
zaps: bool | None = Query(False)
|
zaps: bool | None = Query(False)
|
||||||
disposable: bool | None = Query(True)
|
disposable: bool | None = Query(True)
|
||||||
|
domain: str | None = Query(None)
|
||||||
|
|
||||||
|
|
||||||
class PayLink(BaseModel):
|
class PayLink(BaseModel):
|
||||||
|
|
@ -67,8 +68,25 @@ class PayLink(BaseModel):
|
||||||
success_url: str | None = None
|
success_url: str | None = None
|
||||||
currency: str | None = None
|
currency: str | None = None
|
||||||
fiat_base_multiplier: int | None = None
|
fiat_base_multiplier: int | None = None
|
||||||
|
|
||||||
disposable: bool
|
disposable: bool
|
||||||
|
|
||||||
# TODO deprecated, unused in the code, should be deleted from db.
|
|
||||||
domain: str | None = None
|
domain: str | None = None
|
||||||
|
|
||||||
|
|
||||||
|
class PublicPayLink(BaseModel):
|
||||||
|
id: str
|
||||||
|
username: str | None = None
|
||||||
|
description: str
|
||||||
|
min: float
|
||||||
|
max: float
|
||||||
|
domain: str | None = None
|
||||||
|
currency: str | None = None
|
||||||
|
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: lnurlp://${window.location.hostname}/lnurlp/${paylink_id}"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,26 @@ window.PageLnurlpPublic = {
|
||||||
template: '#page-lnurlp-public',
|
template: '#page-lnurlp-public',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
url: ''
|
url: '',
|
||||||
|
payLink: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setUrl(link_id, domain) {
|
||||||
|
this.url = `https://${domain || window.location.host}/lnurlp/${link_id}`
|
||||||
|
},
|
||||||
|
getPayLink() {
|
||||||
|
this.api
|
||||||
|
.request('GET', `/lnurlp/api/v1/links/public/${this.$route.params.id}`)
|
||||||
|
.then(res => {
|
||||||
|
this.payLink = res.data
|
||||||
|
this.setUrl(this.payLink.id, this.payLink.domain)
|
||||||
|
})
|
||||||
|
.catch(this.utils.notifyApiError)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.url = window.location.origin + '/lnurlp/' + this.$route.params.id
|
this.setUrl(this.$route.params.id)
|
||||||
|
this.getPayLink()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,10 @@ window.PageLnurlp = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
lnaddress(link) {
|
||||||
|
const domain = link.domain || window.location.host
|
||||||
|
return `${link.username}@${domain}`
|
||||||
|
},
|
||||||
mapPayLink(obj) {
|
mapPayLink(obj) {
|
||||||
const locationPath = [
|
const locationPath = [
|
||||||
window.location.protocol,
|
window.location.protocol,
|
||||||
|
|
@ -140,11 +144,13 @@ window.PageLnurlp = {
|
||||||
(link.success_url ? ' and URL "' + link.success_url + '"' : '')
|
(link.success_url ? ' and URL "' + link.success_url + '"' : '')
|
||||||
: 'do nothing',
|
: 'do nothing',
|
||||||
lnurl: link.lnurl,
|
lnurl: link.lnurl,
|
||||||
|
domain: link.domain,
|
||||||
pay_url: link.pay_url,
|
pay_url: link.pay_url,
|
||||||
print_url: link.print_url,
|
print_url: link.print_url,
|
||||||
username: link.username
|
username: link.username
|
||||||
}
|
}
|
||||||
this.activeUrl = window.location.origin + '/lnurlp/' + link.id
|
const domain = link.domain || window.location.host
|
||||||
|
this.activeUrl = `https://${domain}/lnurlp//${link.id}`
|
||||||
this.qrCodeDialog.show = true
|
this.qrCodeDialog.show = true
|
||||||
},
|
},
|
||||||
openUpdateDialog(linkId) {
|
openUpdateDialog(linkId) {
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,6 @@
|
||||||
<span v-text="col.label"></span>
|
<span v-text="col.label"></span>
|
||||||
</q-th>
|
</q-th>
|
||||||
<q-th auto-width></q-th>
|
<q-th auto-width></q-th>
|
||||||
<q-th auto-width></q-th>
|
|
||||||
</q-tr>
|
</q-tr>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:body="props">
|
<template v-slot:body="props">
|
||||||
|
|
@ -55,7 +54,6 @@
|
||||||
><q-tooltip>Shareable Page</q-tooltip></q-btn
|
><q-tooltip>Shareable Page</q-tooltip></q-btn
|
||||||
>
|
>
|
||||||
<q-btn
|
<q-btn
|
||||||
unelevated
|
|
||||||
dense
|
dense
|
||||||
size="xs"
|
size="xs"
|
||||||
icon="visibility"
|
icon="visibility"
|
||||||
|
|
@ -64,6 +62,27 @@
|
||||||
@click="openQrCodeDialog(props.row.id)"
|
@click="openQrCodeDialog(props.row.id)"
|
||||||
><q-tooltip>View Link</q-tooltip></q-btn
|
><q-tooltip>View Link</q-tooltip></q-btn
|
||||||
>
|
>
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
size="xs"
|
||||||
|
@click="openUpdateDialog(props.row.id)"
|
||||||
|
icon="edit"
|
||||||
|
color="light-blue"
|
||||||
|
class="q-ml-sm"
|
||||||
|
>
|
||||||
|
<q-tooltip>Edit</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
size="xs"
|
||||||
|
@click="deletePayLink(props.row.id)"
|
||||||
|
icon="cancel"
|
||||||
|
color="pink"
|
||||||
|
class="q-ml-sm"
|
||||||
|
><q-tooltip>Delete</q-tooltip></q-btn
|
||||||
|
>
|
||||||
</q-td>
|
</q-td>
|
||||||
<q-td
|
<q-td
|
||||||
v-for="col in props.cols"
|
v-for="col in props.cols"
|
||||||
|
|
@ -104,27 +123,6 @@
|
||||||
</q-tooltip>
|
</q-tooltip>
|
||||||
</q-icon>
|
</q-icon>
|
||||||
</q-td>
|
</q-td>
|
||||||
<q-td auto-width>
|
|
||||||
<q-btn
|
|
||||||
flat
|
|
||||||
dense
|
|
||||||
size="xs"
|
|
||||||
@click="openUpdateDialog(props.row.id)"
|
|
||||||
icon="edit"
|
|
||||||
color="light-blue"
|
|
||||||
>
|
|
||||||
<q-tooltip>Edit</q-tooltip>
|
|
||||||
</q-btn>
|
|
||||||
<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-tr>
|
</q-tr>
|
||||||
</template>
|
</template>
|
||||||
</q-table>
|
</q-table>
|
||||||
|
|
@ -402,10 +400,17 @@
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col" style="margin-top: 10px">
|
<div class="col" style="flex: 0 0 auto; margin-top: 10px">
|
||||||
<span class="label">
|
<span class="label"> @ </span>
|
||||||
@ <span v-text="domain"></span>
|
</div>
|
||||||
</span>
|
<div class="col">
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model.trim="formDialog.data.domain"
|
||||||
|
type="text"
|
||||||
|
:label="domain"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row q-col-gutter-sm q-mx-sm">
|
<div class="row q-col-gutter-sm q-mx-sm">
|
||||||
|
|
@ -642,7 +647,7 @@
|
||||||
<span v-text="qrCodeDialog.data.success"></span><br />
|
<span v-text="qrCodeDialog.data.success"></span><br />
|
||||||
<span v-if="qrCodeDialog.data.username">
|
<span v-if="qrCodeDialog.data.username">
|
||||||
<strong>Lightning Address: </strong>
|
<strong>Lightning Address: </strong>
|
||||||
<span v-text="qrCodeDialog.data.username + '@' + domain"></span>
|
<span v-text="lnaddress(qrCodeDialog.data)"></span>
|
||||||
<br />
|
<br />
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
|
||||||
30
views_api.py
30
views_api.py
|
|
@ -23,15 +23,15 @@ from .crud import (
|
||||||
update_lnurlp_settings,
|
update_lnurlp_settings,
|
||||||
update_pay_link,
|
update_pay_link,
|
||||||
)
|
)
|
||||||
from .helpers import lnurl_encode_link_id, parse_nostr_private_key
|
from .helpers import lnurl_encode_link, parse_nostr_private_key
|
||||||
from .models import CreatePayLinkData, LnurlpSettings, PayLink
|
from .models import CreatePayLinkData, LnurlpSettings, PayLink, PublicPayLink
|
||||||
|
|
||||||
lnurlp_api_router = APIRouter()
|
lnurlp_api_router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
def check_lnurl_encode(req: Request, link_id: str) -> str:
|
def check_lnurl_encode(req: Request, link: PayLink) -> str:
|
||||||
try:
|
try:
|
||||||
return lnurl_encode_link_id(req, link_id)
|
return lnurl_encode_link(req, link.id, link.domain)
|
||||||
except InvalidUrl as exc:
|
except InvalidUrl as exc:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
detail=(
|
detail=(
|
||||||
|
|
@ -60,11 +60,11 @@ async def api_links(
|
||||||
|
|
||||||
links = await get_pay_links(wallet_ids)
|
links = await get_pay_links(wallet_ids)
|
||||||
for link in links:
|
for link in links:
|
||||||
link.lnurl = check_lnurl_encode(req=req, link_id=link.id)
|
link.lnurl = check_lnurl_encode(req, link)
|
||||||
return links
|
return links
|
||||||
|
|
||||||
|
|
||||||
@lnurlp_api_router.get("/api/v1/links/{link_id}", status_code=HTTPStatus.OK)
|
@lnurlp_api_router.get("/api/v1/links/{link_id}")
|
||||||
async def api_link_retrieve(
|
async def api_link_retrieve(
|
||||||
req: Request, link_id: str, key_info: WalletTypeInfo = Depends(require_invoice_key)
|
req: Request, link_id: str, key_info: WalletTypeInfo = Depends(require_invoice_key)
|
||||||
) -> PayLink:
|
) -> PayLink:
|
||||||
|
|
@ -85,7 +85,18 @@ async def api_link_retrieve(
|
||||||
detail="Not your pay link.", status_code=HTTPStatus.FORBIDDEN
|
detail="Not your pay link.", status_code=HTTPStatus.FORBIDDEN
|
||||||
)
|
)
|
||||||
|
|
||||||
link.lnurl = check_lnurl_encode(req, link.id)
|
link.lnurl = check_lnurl_encode(req, link)
|
||||||
|
return link
|
||||||
|
|
||||||
|
|
||||||
|
@lnurlp_api_router.get("/api/v1/links/public/{link_id}", response_model=PublicPayLink)
|
||||||
|
async def api_link_public_retrieve(req: Request, link_id: str) -> PayLink:
|
||||||
|
link = await get_pay_link(link_id)
|
||||||
|
if not link:
|
||||||
|
raise HTTPException(
|
||||||
|
detail="Pay link does not exist.", status_code=HTTPStatus.NOT_FOUND
|
||||||
|
)
|
||||||
|
link.lnurl = lnurl_encode_link(req, link.id, link.domain)
|
||||||
return link
|
return link
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -168,7 +179,7 @@ async def api_link_create_or_update(
|
||||||
detail="Wallet does not exist.", status_code=HTTPStatus.FORBIDDEN
|
detail="Wallet does not exist.", status_code=HTTPStatus.FORBIDDEN
|
||||||
)
|
)
|
||||||
|
|
||||||
# admins are allowed to create/edit paylinks beloging to regular users
|
# admins are allowed to create/edit paylinks belonging to regular users
|
||||||
user = await get_user(key_info.wallet.user)
|
user = await get_user(key_info.wallet.user)
|
||||||
admin_user = user.admin if user else False
|
admin_user = user.admin if user else False
|
||||||
if not admin_user and new_wallet.user != key_info.wallet.user:
|
if not admin_user and new_wallet.user != key_info.wallet.user:
|
||||||
|
|
@ -197,8 +208,7 @@ async def api_link_create_or_update(
|
||||||
|
|
||||||
link = await create_pay_link(data)
|
link = await create_pay_link(data)
|
||||||
|
|
||||||
link.lnurl = check_lnurl_encode(req=req, link_id=link.id)
|
link.lnurl = check_lnurl_encode(req, link)
|
||||||
|
|
||||||
return link
|
return link
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ async def api_lnurl_callback(
|
||||||
extra["nostr"] = nostr # put it here for later publishing in tasks.py
|
extra["nostr"] = nostr # put it here for later publishing in tasks.py
|
||||||
|
|
||||||
if link.username:
|
if link.username:
|
||||||
identifier = f"{link.username}@{request.url.netloc}"
|
identifier = f"{link.username}@{link.domain or request.url.netloc}"
|
||||||
text = f"Payment to {link.username}"
|
text = f"Payment to {link.username}"
|
||||||
_metadata = [["text/plain", text], ["text/identifier", identifier]]
|
_metadata = [["text/plain", text], ["text/identifier", identifier]]
|
||||||
extra["lnaddress"] = identifier
|
extra["lnaddress"] = identifier
|
||||||
|
|
@ -173,7 +173,7 @@ async def api_lnurl_response(
|
||||||
callback_url = parse_obj_as(CallbackUrl, str(url))
|
callback_url = parse_obj_as(CallbackUrl, str(url))
|
||||||
|
|
||||||
if link.username:
|
if link.username:
|
||||||
identifier = f"{link.username}@{request.url.netloc}"
|
identifier = f"{link.username}@{link.domain or request.url.netloc}"
|
||||||
text = f"Payment to {link.username}"
|
text = f"Payment to {link.username}"
|
||||||
metadata = [["text/plain", text], ["text/identifier", identifier]]
|
metadata = [["text/plain", text], ["text/identifier", identifier]]
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue