feat: deeplinks support (#58)

* feat: deeplinks support

* fix: move href logic into compute

* chore: fix linting
This commit is contained in:
Tiago Vasconcelos 2025-06-16 14:17:08 +01:00 committed by GitHub
commit b86263cefa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 72 additions and 1 deletions

View file

@ -5,7 +5,7 @@ from fastapi import Query, Request
from lnurl import Lnurl from lnurl import Lnurl
from lnurl import encode as lnurl_encode from lnurl import encode as lnurl_encode
from lnurl.types import LnurlPayMetadata from lnurl.types import LnurlPayMetadata
from pydantic import BaseModel from pydantic import BaseModel, Field
ZERO_KEY = "00000000000000000000000000000000" ZERO_KEY = "00000000000000000000000000000000"
@ -71,3 +71,8 @@ class Refund(BaseModel):
hit_id: str hit_id: str
refund_amount: int refund_amount: int
time: datetime time: datetime
class UIDPost(BaseModel):
UID: str = Field(None, description="The UID of the card.")
LNURLW: str = Field(None, description="The LNURLW of the card.")

View file

@ -148,6 +148,15 @@ window.app = Vue.createApp({
} }
} }
}, },
computed: {
deeplinkUrl() {
const baseUrl = `boltcard://${this.qrCodeDialog.wipe ? 'reset' : 'program'}`
const url =
this.qrCodeDialog.data.link +
(this.qrCodeDialog.wipe ? '&wipe=true' : '')
return `${baseUrl}?url=${encodeURIComponent(url)}`
}
},
methods: { methods: {
readNfcTag() { readNfcTag() {
const ndef = new NDEFReader() const ndef = new NDEFReader()
@ -229,6 +238,9 @@ window.app = Vue.createApp({
this.qrCodeDialog.data = { this.qrCodeDialog.data = {
id: card.id, id: card.id,
link: window.location.origin + '/boltcards/api/v1/auth?a=' + card.otp, link: window.location.origin + '/boltcards/api/v1/auth?a=' + card.otp,
encodedURI: encodeURIComponent(
window.location.origin + '/boltcards/api/v1/auth?a=' + card.otp
),
name: card.card_name, name: card.card_name,
uid: card.uid, uid: card.uid,
external_id: card.external_id, external_id: card.external_id,

View file

@ -463,6 +463,7 @@
<q-tooltip>Click to copy, then paste to NFC Card Creator</q-tooltip> <q-tooltip>Click to copy, then paste to NFC Card Creator</q-tooltip>
</q-btn> </q-btn>
<q-btn <q-btn
class="q-ml-sm"
unelevated unelevated
outline outline
color="red" color="red"
@ -473,6 +474,17 @@
> >
<q-tooltip>Backup the keys, or wipe the card first!</q-tooltip> <q-tooltip>Backup the keys, or wipe the card first!</q-tooltip>
</q-btn> </q-btn>
<q-btn
class="q-ml-sm"
unelevated
outline
color="grey"
:href="deeplinkUrl"
target="_blank"
label="Use Boltcard App"
>
<q-tooltip>Use Boltcard Programmer App</q-tooltip>
</q-btn>
<div class="row q-mt-lg q-gutter-sm"> <div class="row q-mt-lg q-gutter-sm">
<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>

View file

@ -16,12 +16,14 @@ from .crud import (
get_card, get_card,
get_card_by_external_id, get_card_by_external_id,
get_card_by_otp, get_card_by_otp,
get_card_by_uid,
get_hit, get_hit,
get_hits_today, get_hits_today,
spend_hit, spend_hit,
update_card_counter, update_card_counter,
update_card_otp, update_card_otp,
) )
from .models import UIDPost
from .nxp424 import decrypt_sun, get_sun_mac from .nxp424 import decrypt_sun, get_sun_mac
boltcards_lnurl_router = APIRouter() boltcards_lnurl_router = APIRouter()
@ -179,6 +181,46 @@ async def api_auth(a, request: Request):
return response return response
# /boltcards/api/v1/auth?a=00000000000000000000000000000000
@boltcards_lnurl_router.post("/api/v1/auth")
async def api_auth_post(a: str, request: Request, data: UIDPost, wipe: bool = False):
card = None
if wipe:
card = await get_card_by_otp(a)
else:
if not data.UID:
raise HTTPException(
detail="Missing UID.", status_code=HTTPStatus.BAD_REQUEST
)
card = await get_card_by_uid(data.UID)
if not card:
raise HTTPException(
detail="Card does not exist.", status_code=HTTPStatus.NOT_FOUND
)
new_otp = secrets.token_hex(16)
await update_card_otp(new_otp, card.id)
lnurlw_base = (
f"{urlparse(str(request.url)).netloc}/boltcards/api/v1/scan/{card.external_id}"
)
response = {
"CARD_NAME": card.card_name,
"ID": str(1),
"K0": card.k0,
"K1": card.k1,
"K2": card.k2,
"K3": card.k1,
"K4": card.k2,
"LNURLW_BASE": "LNURLW://" + lnurlw_base,
"LNURLW": "LNURLW://" + lnurlw_base,
"PROTOCOL_NAME": "NEW_BOLT_CARD_RESPONSE",
"PROTOCOL_VERSION": str(1),
}
if wipe:
response["action"] = "wipe"
return response
###############LNURLPAY REFUNDS################# ###############LNURLPAY REFUNDS#################