Black/prettier
This commit is contained in:
parent
9e3cd3f00e
commit
7bfc0848ef
|
@ -16,9 +16,11 @@ boltcards_static_files = [
|
|||
|
||||
boltcards_ext: APIRouter = APIRouter(prefix="/boltcards", tags=["boltcards"])
|
||||
|
||||
|
||||
def boltcards_renderer():
|
||||
return template_renderer(["lnbits/extensions/boltcards/templates"])
|
||||
|
||||
|
||||
from .lnurl import * # noqa
|
||||
from .tasks import * # noqa
|
||||
from .views import * # noqa
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
"name": "Bolt Cards",
|
||||
"short_description": "Self custody Bolt Cards with one time LNURLw",
|
||||
"icon": "payment",
|
||||
"contributors": ["iwarpbtc"]
|
||||
"contributors": ["iwarpbtc", "arcbtc", "leesalminen"]
|
||||
}
|
||||
|
|
|
@ -116,7 +116,9 @@ async def delete_card(card_id: str) -> None:
|
|||
# Delete refunds
|
||||
refunds = await get_refunds([hit])
|
||||
for refund in refunds:
|
||||
await db.execute("DELETE FROM boltcards.refunds WHERE id = ?", (refund.hit_id,))
|
||||
await db.execute(
|
||||
"DELETE FROM boltcards.refunds WHERE id = ?", (refund.hit_id,)
|
||||
)
|
||||
|
||||
|
||||
async def update_card_counter(counter: int, id: str):
|
||||
|
@ -125,6 +127,7 @@ async def update_card_counter(counter: int, id: str):
|
|||
(counter, id),
|
||||
)
|
||||
|
||||
|
||||
async def enable_disable_card(enable: bool, id: str) -> Optional[Card]:
|
||||
row = await db.execute(
|
||||
"UPDATE boltcards.cards SET enable = ? WHERE id = ?",
|
||||
|
@ -132,6 +135,7 @@ async def enable_disable_card(enable: bool, id: str) -> Optional[Card]:
|
|||
)
|
||||
return await get_card(id)
|
||||
|
||||
|
||||
async def update_card_otp(otp: str, id: str):
|
||||
await db.execute(
|
||||
"UPDATE boltcards.cards SET otp = ? WHERE id = ?",
|
||||
|
@ -157,19 +161,23 @@ async def get_hits(cards_ids: Union[str, List[str]]) -> List[Hit]:
|
|||
|
||||
return [Hit(**row) for row in rows]
|
||||
|
||||
|
||||
async def get_hits_today(card_id: Union[str, List[str]]) -> List[Hit]:
|
||||
rows = await db.fetchall(
|
||||
f"SELECT * FROM boltcards.hits WHERE card_id = ? AND time >= DATE('now') AND time < DATE('now', '+1 day')", (card_id,)
|
||||
f"SELECT * FROM boltcards.hits WHERE card_id = ? AND time >= DATE('now') AND time < DATE('now', '+1 day')",
|
||||
(card_id,),
|
||||
)
|
||||
|
||||
return [Hit(**row) for row in rows]
|
||||
|
||||
|
||||
async def spend_hit(id: str):
|
||||
await db.execute(
|
||||
"UPDATE boltcards.hits SET spent = ? WHERE id = ?",
|
||||
(True, id),
|
||||
)
|
||||
|
||||
|
||||
async def create_hit(card_id, ip, useragent, old_ctr, new_ctr) -> Hit:
|
||||
hit_id = urlsafe_short_hash()
|
||||
await db.execute(
|
||||
|
@ -201,6 +209,7 @@ async def create_hit(card_id, ip, useragent, old_ctr, new_ctr) -> Hit:
|
|||
assert hit, "Newly recorded hit couldn't be retrieved"
|
||||
return hit
|
||||
|
||||
|
||||
async def create_refund(hit_id, refund_amount) -> Refund:
|
||||
refund_id = urlsafe_short_hash()
|
||||
await db.execute(
|
||||
|
@ -224,13 +233,17 @@ async def create_refund(hit_id, refund_amount) -> Refund:
|
|||
assert refund, "Newly recorded hit couldn't be retrieved"
|
||||
return refund
|
||||
|
||||
|
||||
async def get_refund(refund_id: str) -> Optional[Refund]:
|
||||
row = await db.fetchone(f"SELECT * FROM boltcards.refunds WHERE id = ?", (refund_id))
|
||||
row = await db.fetchone(
|
||||
f"SELECT * FROM boltcards.refunds WHERE id = ?", (refund_id)
|
||||
)
|
||||
if not row:
|
||||
return None
|
||||
refund = dict(**row)
|
||||
return Refund.parse_obj(refund)
|
||||
|
||||
|
||||
async def get_refunds(hits_ids: Union[str, List[str]]) -> List[Refund]:
|
||||
q = ",".join(["?"] * len(hits_ids))
|
||||
rows = await db.fetchall(
|
||||
|
|
|
@ -95,15 +95,14 @@ async def api_scan(p, c, request: Request, card_uid: str = None):
|
|||
lnurlpay = lnurl_encode(request.url_for("boltcards.lnurlp_response", hit_id=hit.id))
|
||||
return {
|
||||
"tag": "withdrawRequest",
|
||||
"callback": request.url_for(
|
||||
"boltcards.lnurl_callback", hitid=hit.id
|
||||
),
|
||||
"callback": request.url_for("boltcards.lnurl_callback", hitid=hit.id),
|
||||
"k1": hit.id,
|
||||
"minWithdrawable": 1 * 1000,
|
||||
"maxWithdrawable": card.tx_limit * 1000,
|
||||
"defaultDescription": f"Boltcard (refund address {lnurlpay})",
|
||||
}
|
||||
|
||||
|
||||
@boltcards_ext.get(
|
||||
"/api/v1/lnurl/cb/{hitid}",
|
||||
status_code=HTTPStatus.OK,
|
||||
|
@ -158,8 +157,10 @@ async def api_auth(a, request: Request):
|
|||
|
||||
return response
|
||||
|
||||
|
||||
###############LNURLPAY REFUNDS#################
|
||||
|
||||
|
||||
@boltcards_ext.get(
|
||||
"/api/v1/lnurlp/{hit_id}",
|
||||
response_class=HTMLResponse,
|
||||
|
@ -199,14 +200,12 @@ async def lnurlp_callback(
|
|||
wallet_id=card.wallet,
|
||||
amount=int(amount) / 1000,
|
||||
memo=f"Refund {hit_id}",
|
||||
unhashed_description=LnurlPayMetadata(json.dumps([["text/plain", "Refund"]])).encode("utf-8"),
|
||||
unhashed_description=LnurlPayMetadata(
|
||||
json.dumps([["text/plain", "Refund"]])
|
||||
).encode("utf-8"),
|
||||
extra={"refund": hit_id},
|
||||
)
|
||||
|
||||
payResponse = {"pr": payment_request, "successAction": success_action, "routes": []}
|
||||
|
||||
return json.dumps(payResponse)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -36,14 +36,13 @@ class Card(BaseModel):
|
|||
return cls(**dict(row))
|
||||
|
||||
def lnurl(self, req: Request) -> Lnurl:
|
||||
url = req.url_for(
|
||||
"boltcard.lnurl_response", device_id=self.id, _external=True
|
||||
)
|
||||
url = req.url_for("boltcard.lnurl_response", device_id=self.id, _external=True)
|
||||
return lnurl_encode(url)
|
||||
|
||||
async def lnurlpay_metadata(self) -> LnurlPayMetadata:
|
||||
return LnurlPayMetadata(json.dumps([["text/plain", self.title]]))
|
||||
|
||||
|
||||
class CreateCardData(BaseModel):
|
||||
card_name: str = Query(...)
|
||||
uid: str = Query(...)
|
||||
|
@ -58,6 +57,7 @@ class CreateCardData(BaseModel):
|
|||
prev_k1: str = Query(ZERO_KEY)
|
||||
prev_k2: str = Query(ZERO_KEY)
|
||||
|
||||
|
||||
class Hit(BaseModel):
|
||||
id: str
|
||||
card_id: str
|
||||
|
@ -72,6 +72,7 @@ class Hit(BaseModel):
|
|||
def from_row(cls, row: Row) -> "Hit":
|
||||
return cls(**dict(row))
|
||||
|
||||
|
||||
class Refund(BaseModel):
|
||||
id: str
|
||||
hit_id: str
|
||||
|
|
|
@ -9,7 +9,6 @@ const mapCards = obj => {
|
|||
return obj
|
||||
}
|
||||
|
||||
|
||||
new Vue({
|
||||
el: '#vue',
|
||||
mixins: [windowMixin],
|
||||
|
@ -23,11 +22,12 @@ new Vue({
|
|||
cardDialog: {
|
||||
show: false,
|
||||
data: {
|
||||
counter:1,
|
||||
counter: 1,
|
||||
k0: '',
|
||||
k1: '',
|
||||
k2: '',
|
||||
card_name:''},
|
||||
card_name: ''
|
||||
},
|
||||
temp: {}
|
||||
},
|
||||
cardsTable: {
|
||||
|
@ -190,7 +190,7 @@ new Vue({
|
|||
})
|
||||
})
|
||||
},
|
||||
openQrCodeDialog(cardId) {
|
||||
openQrCodeDialog (cardId) {
|
||||
var card = _.findWhere(this.cards, {id: cardId})
|
||||
this.qrCodeDialog.data = {
|
||||
link: window.location.origin + '/boltcards/api/v1/auth?a=' + card.otp,
|
||||
|
@ -352,7 +352,7 @@ new Vue({
|
|||
},
|
||||
exportRefundsCSV: function () {
|
||||
LNbits.utils.exportCSV(this.refundsTable.columns, this.refunds)
|
||||
},
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
if (this.g.user.wallets.length) {
|
||||
|
|
|
@ -27,8 +27,9 @@ async def on_invoice_paid(payment: Payment) -> None:
|
|||
if payment.extra.get("wh_status"):
|
||||
# this webhook has already been sent
|
||||
return
|
||||
hit = await get_hit(payment.extra.get("tag")[7:len(payment.extra.get("tag"))])
|
||||
hit = await get_hit(payment.extra.get("tag")[7 : len(payment.extra.get("tag"))])
|
||||
if hit:
|
||||
refund = await create_refund(hit_id=hit.id, refund_amount=payment.extra.get("amount"))
|
||||
refund = await create_refund(
|
||||
hit_id=hit.id, refund_amount=payment.extra.get("amount")
|
||||
)
|
||||
await mark_webhook_sent(payment, 1)
|
||||
|
||||
|
|
|
@ -15,10 +15,6 @@
|
|||
>More details</a
|
||||
>
|
||||
<br />
|
||||
<small>
|
||||
Created by,
|
||||
<a href="https://twitter.com/btcslovnik">iWarp</a></small
|
||||
>
|
||||
</p>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
|
|
|
@ -8,11 +8,18 @@
|
|||
<div class="row items-center no-wrap q-mb-md">
|
||||
<div class="col">
|
||||
<div class="row justify-start" style="width:150px">
|
||||
<div class="col" >
|
||||
<div class="col">
|
||||
<h5 class="text-subtitle1 q-my-none">Cards</h5>
|
||||
</div>
|
||||
<div class="col">
|
||||
<q-btn round size="sm" icon="add" unelevated color="primary" @click="addCardOpen">
|
||||
<q-btn
|
||||
round
|
||||
size="sm"
|
||||
icon="add"
|
||||
unelevated
|
||||
color="primary"
|
||||
@click="addCardOpen"
|
||||
>
|
||||
<q-tooltip>Add card</q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
|
@ -53,14 +60,18 @@
|
|||
icon="qr_code"
|
||||
:color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
|
||||
@click="openQrCodeDialog(props.row.id)"
|
||||
><q-tooltip>Card key credentials</q-tooltip></q-btn>
|
||||
><q-tooltip>Card key credentials</q-tooltip></q-btn
|
||||
>
|
||||
</q-td>
|
||||
<q-td auto-width>
|
||||
<q-btn
|
||||
outline
|
||||
color="grey"
|
||||
@click="copyText(lnurlLink + props.row.uid)"
|
||||
lnurlLink >lnurl://...<q-tooltip>Click to copy, then add to NFC card</q-tooltip>
|
||||
lnurlLink
|
||||
>lnurl://...<q-tooltip
|
||||
>Click to copy, then add to NFC card</q-tooltip
|
||||
>
|
||||
</q-btn>
|
||||
</q-td>
|
||||
<q-td v-for="col in props.cols" :key="col.name" :props="props">
|
||||
|
@ -72,13 +83,15 @@
|
|||
dense
|
||||
@click="enableCard(props.row.wallet, props.row.id, false)"
|
||||
color="pink"
|
||||
>DISABLE</q-btn>
|
||||
>DISABLE</q-btn
|
||||
>
|
||||
<q-btn
|
||||
v-else
|
||||
dense
|
||||
@click="enableCard(props.row.wallet, props.row.id, true)"
|
||||
color="green"
|
||||
>ENABLE</q-btn>
|
||||
>ENABLE</q-btn
|
||||
>
|
||||
</q-td>
|
||||
<q-td auto-width>
|
||||
<q-btn
|
||||
|
@ -88,7 +101,8 @@
|
|||
@click="updateCardDialog(props.row.id)"
|
||||
icon="edit"
|
||||
color="light-blue"
|
||||
><q-tooltip>Edit card</q-tooltip></q-btn>
|
||||
><q-tooltip>Edit card</q-tooltip></q-btn
|
||||
>
|
||||
</q-td>
|
||||
<q-td auto-width>
|
||||
<q-btn
|
||||
|
@ -98,7 +112,10 @@
|
|||
@click="deleteCard(props.row.id)"
|
||||
icon="cancel"
|
||||
color="pink"
|
||||
><q-tooltip>Deleting card will also delete all records</q-tooltip></q-btn>
|
||||
><q-tooltip
|
||||
>Deleting card will also delete all records</q-tooltip
|
||||
></q-btn
|
||||
>
|
||||
</q-td>
|
||||
</q-tr>
|
||||
</template>
|
||||
|
@ -243,8 +260,6 @@
|
|||
></q-input>
|
||||
<div class="row">
|
||||
<div class="col-10">
|
||||
|
||||
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
|
@ -252,9 +267,10 @@
|
|||
v-model.trim="cardDialog.data.uid"
|
||||
type="text"
|
||||
label="Card UID "
|
||||
><q-tooltip>Get from the card you'll use, using an NFC app</q-tooltip></q-input>
|
||||
|
||||
|
||||
><q-tooltip
|
||||
>Get from the card you'll use, using an NFC app</q-tooltip
|
||||
></q-input
|
||||
>
|
||||
</div>
|
||||
<div class="col-2 q-pl-sm">
|
||||
<q-btn
|
||||
|
@ -263,11 +279,11 @@
|
|||
color="grey"
|
||||
icon="nfc"
|
||||
:disable="nfcTagReading"
|
||||
><q-tooltip>Tap card to scan UID (coming soon)</q-tooltip></q-btn>
|
||||
><q-tooltip>Tap card to scan UID (coming soon)</q-tooltip></q-btn
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<q-toggle
|
||||
v-model="toggleAdvanced"
|
||||
label="Show advanced options"
|
||||
|
@ -276,7 +292,6 @@
|
|||
<q-input
|
||||
filled
|
||||
dense
|
||||
|
||||
v-model.trim="cardDialog.data.k0"
|
||||
type="text"
|
||||
label="Card Auth key (K0)"
|
||||
|
@ -287,7 +302,6 @@
|
|||
<q-input
|
||||
filled
|
||||
dense
|
||||
|
||||
v-model.trim="cardDialog.data.k1"
|
||||
type="text"
|
||||
label="Card Meta key (K1)"
|
||||
|
@ -296,7 +310,6 @@
|
|||
<q-input
|
||||
filled
|
||||
dense
|
||||
|
||||
v-model.trim="cardDialog.data.k2"
|
||||
type="text"
|
||||
label="Card File key (K2)"
|
||||
|
|
|
@ -29,6 +29,7 @@ from .nxp424 import decryptSUN, getSunMAC
|
|||
|
||||
from loguru import logger
|
||||
|
||||
|
||||
@boltcards_ext.get("/api/v1/cards")
|
||||
async def api_cards(
|
||||
g: WalletTypeInfo = Depends(get_key_type), all_wallets: bool = Query(False)
|
||||
|
@ -89,6 +90,7 @@ async def api_card_create_or_update(
|
|||
card = await create_card(wallet_id=wallet.wallet.id, data=data)
|
||||
return card.dict()
|
||||
|
||||
|
||||
@boltcards_ext.get("/api/v1/cards/enable/{card_id}/{enable}", status_code=HTTPStatus.OK)
|
||||
async def enable_card(
|
||||
card_id,
|
||||
|
@ -97,16 +99,13 @@ async def enable_card(
|
|||
):
|
||||
card = await get_card(card_id)
|
||||
if not card:
|
||||
raise HTTPException(
|
||||
detail="No card found.", status_code=HTTPStatus.NOT_FOUND
|
||||
)
|
||||
raise HTTPException(detail="No card found.", status_code=HTTPStatus.NOT_FOUND)
|
||||
if card.wallet != wallet.wallet.id:
|
||||
raise HTTPException(
|
||||
detail="Not your card.", status_code=HTTPStatus.FORBIDDEN
|
||||
)
|
||||
raise HTTPException(detail="Not your card.", status_code=HTTPStatus.FORBIDDEN)
|
||||
card = await enable_disable_card(enable=enable, id=card_id)
|
||||
return card.dict()
|
||||
|
||||
|
||||
@boltcards_ext.delete("/api/v1/cards/{card_id}")
|
||||
async def api_card_delete(card_id, wallet: WalletTypeInfo = Depends(require_admin_key)):
|
||||
card = await get_card(card_id)
|
||||
|
@ -139,6 +138,7 @@ async def api_hits(
|
|||
|
||||
return [hit.dict() for hit in await get_hits(cards_ids)]
|
||||
|
||||
|
||||
@boltcards_ext.get("/api/v1/refunds")
|
||||
async def api_hits(
|
||||
g: WalletTypeInfo = Depends(get_key_type), all_wallets: bool = Query(False)
|
||||
|
|
Loading…
Reference in New Issue
Block a user