mirror of
https://github.com/lnbits/lnbits-legend.git
synced 2025-02-24 22:58:46 +01:00
Added enable disable card
This commit is contained in:
parent
20dbd879b1
commit
977f9bb8c4
7 changed files with 92 additions and 14 deletions
|
@ -19,12 +19,13 @@ async def create_card(data: CreateCardData, wallet_id: str) -> Card:
|
||||||
counter,
|
counter,
|
||||||
tx_limit,
|
tx_limit,
|
||||||
daily_limit,
|
daily_limit,
|
||||||
|
enable,
|
||||||
k0,
|
k0,
|
||||||
k1,
|
k1,
|
||||||
k2,
|
k2,
|
||||||
otp
|
otp
|
||||||
)
|
)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
""",
|
""",
|
||||||
(
|
(
|
||||||
card_id,
|
card_id,
|
||||||
|
@ -34,6 +35,7 @@ async def create_card(data: CreateCardData, wallet_id: str) -> Card:
|
||||||
data.counter,
|
data.counter,
|
||||||
data.tx_limit,
|
data.tx_limit,
|
||||||
data.daily_limit,
|
data.daily_limit,
|
||||||
|
True,
|
||||||
data.k0,
|
data.k0,
|
||||||
data.k1,
|
data.k1,
|
||||||
data.k2,
|
data.k2,
|
||||||
|
@ -104,7 +106,17 @@ async def get_card_by_otp(otp: str) -> Optional[Card]:
|
||||||
|
|
||||||
|
|
||||||
async def delete_card(card_id: str) -> None:
|
async def delete_card(card_id: str) -> None:
|
||||||
|
# Delete cards
|
||||||
|
card = await get_card(card_id)
|
||||||
await db.execute("DELETE FROM boltcards.cards WHERE id = ?", (card_id,))
|
await db.execute("DELETE FROM boltcards.cards WHERE id = ?", (card_id,))
|
||||||
|
# Delete hits
|
||||||
|
hits = await get_hits([card_id])
|
||||||
|
for hit in hits:
|
||||||
|
await db.execute("DELETE FROM boltcards.hits WHERE id = ?", (hit.id,))
|
||||||
|
# Delete refunds
|
||||||
|
refunds = await get_refunds([hit])
|
||||||
|
for refund in refunds:
|
||||||
|
await db.execute("DELETE FROM boltcards.refunds WHERE id = ?", (refund.hit_id,))
|
||||||
|
|
||||||
|
|
||||||
async def update_card_counter(counter: int, id: str):
|
async def update_card_counter(counter: int, id: str):
|
||||||
|
@ -113,6 +125,12 @@ async def update_card_counter(counter: int, id: str):
|
||||||
(counter, id),
|
(counter, id),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def enable_disable_card(enable: bool, id: str) -> Optional[Card]:
|
||||||
|
row = await db.execute(
|
||||||
|
"UPDATE boltcards.cards SET enable = ? WHERE id = ?",
|
||||||
|
(enable, id),
|
||||||
|
)
|
||||||
|
return await get_card(id)
|
||||||
|
|
||||||
async def update_card_otp(otp: str, id: str):
|
async def update_card_otp(otp: str, id: str):
|
||||||
await db.execute(
|
await db.execute(
|
||||||
|
|
|
@ -57,6 +57,8 @@ async def api_scan(p, c, request: Request, card_id: str = None):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
card = await get_card_by_uid(card_uid)
|
card = await get_card_by_uid(card_uid)
|
||||||
|
if not card.enable:
|
||||||
|
return {"status": "ERROR", "reason": "Card is disabled."}
|
||||||
card_uid, counter = decryptSUN(bytes.fromhex(p), bytes.fromhex(card.k1))
|
card_uid, counter = decryptSUN(bytes.fromhex(p), bytes.fromhex(card.k1))
|
||||||
if card.uid.upper() != card_uid.hex().upper():
|
if card.uid.upper() != card_uid.hex().upper():
|
||||||
return {"status": "ERROR", "reason": "Card UID mis-match."}
|
return {"status": "ERROR", "reason": "Card UID mis-match."}
|
||||||
|
@ -173,6 +175,8 @@ async def lnurlp_response(req: Request, hit_id: str = Query(None)):
|
||||||
card = await get_card(hit.card_id)
|
card = await get_card(hit.card_id)
|
||||||
if not hit:
|
if not hit:
|
||||||
return {"status": "ERROR", "reason": f"LNURL-pay record not found."}
|
return {"status": "ERROR", "reason": f"LNURL-pay record not found."}
|
||||||
|
if not card.enable:
|
||||||
|
return {"status": "ERROR", "reason": "Card is disabled."}
|
||||||
payResponse = {
|
payResponse = {
|
||||||
"tag": "payRequest",
|
"tag": "payRequest",
|
||||||
"callback": req.url_for("boltcards.lnurlp_callback", hit_id=hit_id),
|
"callback": req.url_for("boltcards.lnurlp_callback", hit_id=hit_id),
|
||||||
|
|
|
@ -12,6 +12,7 @@ async def m001_initial(db):
|
||||||
counter INT NOT NULL DEFAULT 0,
|
counter INT NOT NULL DEFAULT 0,
|
||||||
tx_limit TEXT NOT NULL,
|
tx_limit TEXT NOT NULL,
|
||||||
daily_limit TEXT NOT NULL,
|
daily_limit TEXT NOT NULL,
|
||||||
|
enable BOOL NOT NULL,
|
||||||
k0 TEXT NOT NULL DEFAULT '00000000000000000000000000000000',
|
k0 TEXT NOT NULL DEFAULT '00000000000000000000000000000000',
|
||||||
k1 TEXT NOT NULL DEFAULT '00000000000000000000000000000000',
|
k1 TEXT NOT NULL DEFAULT '00000000000000000000000000000000',
|
||||||
k2 TEXT NOT NULL DEFAULT '00000000000000000000000000000000',
|
k2 TEXT NOT NULL DEFAULT '00000000000000000000000000000000',
|
||||||
|
|
|
@ -22,6 +22,7 @@ class Card(BaseModel):
|
||||||
counter: int
|
counter: int
|
||||||
tx_limit: int
|
tx_limit: int
|
||||||
daily_limit: int
|
daily_limit: int
|
||||||
|
enable: bool
|
||||||
k0: str
|
k0: str
|
||||||
k1: str
|
k1: str
|
||||||
k2: str
|
k2: str
|
||||||
|
@ -49,6 +50,7 @@ class CreateCardData(BaseModel):
|
||||||
counter: int = Query(0)
|
counter: int = Query(0)
|
||||||
tx_limit: int = Query(0)
|
tx_limit: int = Query(0)
|
||||||
daily_limit: int = Query(0)
|
daily_limit: int = Query(0)
|
||||||
|
enable: bool = Query(...)
|
||||||
k0: str = Query(ZERO_KEY)
|
k0: str = Query(ZERO_KEY)
|
||||||
k1: str = Query(ZERO_KEY)
|
k1: str = Query(ZERO_KEY)
|
||||||
k2: str = Query(ZERO_KEY)
|
k2: str = Query(ZERO_KEY)
|
||||||
|
|
|
@ -16,6 +16,7 @@ new Vue({
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
toggleAdvanced: false,
|
toggleAdvanced: false,
|
||||||
|
nfcTagReading: false,
|
||||||
cards: [],
|
cards: [],
|
||||||
hits: [],
|
hits: [],
|
||||||
refunds: [],
|
refunds: [],
|
||||||
|
@ -194,6 +195,7 @@ new Vue({
|
||||||
this.generateKeys()
|
this.generateKeys()
|
||||||
},
|
},
|
||||||
generateKeys: function () {
|
generateKeys: function () {
|
||||||
|
var self = this
|
||||||
const genRanHex = size =>
|
const genRanHex = size =>
|
||||||
[...Array(size)]
|
[...Array(size)]
|
||||||
.map(() => Math.floor(Math.random() * 16).toString(16))
|
.map(() => Math.floor(Math.random() * 16).toString(16))
|
||||||
|
@ -203,20 +205,17 @@ new Vue({
|
||||||
typeof this.cardDialog.data.card_name === 'string' &&
|
typeof this.cardDialog.data.card_name === 'string' &&
|
||||||
this.cardDialog.data.card_name.search('debug') > -1
|
this.cardDialog.data.card_name.search('debug') > -1
|
||||||
|
|
||||||
this.cardDialog.data.k0 = debugcard
|
self.cardDialog.data.k0 = debugcard
|
||||||
? '11111111111111111111111111111111'
|
? '11111111111111111111111111111111'
|
||||||
: genRanHex(32)
|
: genRanHex(32)
|
||||||
this.$refs['k0'].value = this.cardDialog.data.k0
|
|
||||||
|
|
||||||
this.cardDialog.data.k1 = debugcard
|
self.cardDialog.data.k1 = debugcard
|
||||||
? '22222222222222222222222222222222'
|
? '22222222222222222222222222222222'
|
||||||
: genRanHex(32)
|
: genRanHex(32)
|
||||||
this.$refs['k1'].value = this.cardDialog.data.k1
|
|
||||||
|
|
||||||
this.cardDialog.data.k2 = debugcard
|
self.cardDialog.data.k2 = debugcard
|
||||||
? '33333333333333333333333333333333'
|
? '33333333333333333333333333333333'
|
||||||
: genRanHex(32)
|
: genRanHex(32)
|
||||||
this.$refs['k2'].value = this.cardDialog.data.k2
|
|
||||||
},
|
},
|
||||||
closeFormDialog: function () {
|
closeFormDialog: function () {
|
||||||
this.cardDialog.data = {}
|
this.cardDialog.data = {}
|
||||||
|
@ -288,6 +287,28 @@ new Vue({
|
||||||
LNbits.utils.notifyApiError(error)
|
LNbits.utils.notifyApiError(error)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
enableCard: function (wallet, card_id, enable) {
|
||||||
|
var self = this
|
||||||
|
let fullWallet = _.findWhere(self.g.user.wallets, {
|
||||||
|
id: wallet
|
||||||
|
})
|
||||||
|
LNbits.api
|
||||||
|
.request(
|
||||||
|
'GET',
|
||||||
|
'/boltcards/api/v1/cards/enable/' + card_id + '/' + enable,
|
||||||
|
fullWallet.adminkey
|
||||||
|
)
|
||||||
|
.then(function (response) {
|
||||||
|
console.log(response.data)
|
||||||
|
self.cards = _.reject(self.cards, function (obj) {
|
||||||
|
return obj.id == response.data.id
|
||||||
|
})
|
||||||
|
self.cards.push(mapCards(response.data))
|
||||||
|
})
|
||||||
|
.catch(function (error) {
|
||||||
|
LNbits.utils.notifyApiError(error)
|
||||||
|
})
|
||||||
|
},
|
||||||
deleteCard: function (cardId) {
|
deleteCard: function (cardId) {
|
||||||
let self = this
|
let self = this
|
||||||
let cards = _.findWhere(this.cards, {id: cardId})
|
let cards = _.findWhere(this.cards, {id: cardId})
|
||||||
|
|
|
@ -65,6 +65,20 @@
|
||||||
<q-td v-for="col in props.cols" :key="col.name" :props="props">
|
<q-td v-for="col in props.cols" :key="col.name" :props="props">
|
||||||
{{ col.value }}
|
{{ col.value }}
|
||||||
</q-td>
|
</q-td>
|
||||||
|
<q-td auto-width>
|
||||||
|
<q-btn
|
||||||
|
v-if="props.row.enable"
|
||||||
|
dense
|
||||||
|
@click="enableCard(props.row.wallet, props.row.id, false)"
|
||||||
|
color="pink"
|
||||||
|
>DISABLE</q-btn>
|
||||||
|
<q-btn
|
||||||
|
v-else
|
||||||
|
dense
|
||||||
|
@click="enableCard(props.row.wallet, props.row.id, true)"
|
||||||
|
color="green"
|
||||||
|
>ENABLE</q-btn>
|
||||||
|
</q-td>
|
||||||
<q-td auto-width>
|
<q-td auto-width>
|
||||||
<q-btn
|
<q-btn
|
||||||
flat
|
flat
|
||||||
|
@ -73,8 +87,7 @@
|
||||||
@click="updateCardDialog(props.row.id)"
|
@click="updateCardDialog(props.row.id)"
|
||||||
icon="edit"
|
icon="edit"
|
||||||
color="light-blue"
|
color="light-blue"
|
||||||
>
|
><q-tooltip>Edit card</q-tooltip></q-btn>
|
||||||
</q-btn>
|
|
||||||
</q-td>
|
</q-td>
|
||||||
<q-td auto-width>
|
<q-td auto-width>
|
||||||
<q-btn
|
<q-btn
|
||||||
|
@ -84,7 +97,7 @@
|
||||||
@click="deleteCard(props.row.id)"
|
@click="deleteCard(props.row.id)"
|
||||||
icon="cancel"
|
icon="cancel"
|
||||||
color="pink"
|
color="pink"
|
||||||
></q-btn>
|
><q-tooltip>Deleting card will also delete all records</q-tooltip></q-btn>
|
||||||
</q-td>
|
</q-td>
|
||||||
</q-tr>
|
</q-tr>
|
||||||
</template>
|
</template>
|
||||||
|
@ -255,7 +268,6 @@
|
||||||
|
|
||||||
|
|
||||||
<q-toggle
|
<q-toggle
|
||||||
@click="toggleKeys"
|
|
||||||
v-model="toggleAdvanced"
|
v-model="toggleAdvanced"
|
||||||
label="Show advanced options"
|
label="Show advanced options"
|
||||||
></q-toggle>
|
></q-toggle>
|
||||||
|
@ -263,7 +275,7 @@
|
||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
dense
|
dense
|
||||||
ref="k0"
|
|
||||||
v-model.trim="cardDialog.data.k0"
|
v-model.trim="cardDialog.data.k0"
|
||||||
type="text"
|
type="text"
|
||||||
label="Card Auth key (K0)"
|
label="Card Auth key (K0)"
|
||||||
|
@ -274,7 +286,7 @@
|
||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
dense
|
dense
|
||||||
ref="k1"
|
|
||||||
v-model.trim="cardDialog.data.k1"
|
v-model.trim="cardDialog.data.k1"
|
||||||
type="text"
|
type="text"
|
||||||
label="Card Meta key (K1)"
|
label="Card Meta key (K1)"
|
||||||
|
@ -283,7 +295,7 @@
|
||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
dense
|
dense
|
||||||
ref="k2"
|
|
||||||
v-model.trim="cardDialog.data.k2"
|
v-model.trim="cardDialog.data.k2"
|
||||||
type="text"
|
type="text"
|
||||||
label="Card File key (K2)"
|
label="Card File key (K2)"
|
||||||
|
|
|
@ -21,6 +21,7 @@ from .crud import (
|
||||||
update_card,
|
update_card,
|
||||||
update_card_counter,
|
update_card_counter,
|
||||||
update_card_otp,
|
update_card_otp,
|
||||||
|
enable_disable_card,
|
||||||
get_refunds,
|
get_refunds,
|
||||||
)
|
)
|
||||||
from .models import CreateCardData
|
from .models import CreateCardData
|
||||||
|
@ -89,6 +90,25 @@ async def api_card_create_or_update(
|
||||||
card = await create_card(wallet_id=wallet.wallet.id, data=data)
|
card = await create_card(wallet_id=wallet.wallet.id, data=data)
|
||||||
return card.dict()
|
return card.dict()
|
||||||
|
|
||||||
|
@boltcards_ext.get("/api/v1/cards/enable/{card_id}/{enable}", status_code=HTTPStatus.OK)
|
||||||
|
async def enable_card(
|
||||||
|
card_id,
|
||||||
|
enable,
|
||||||
|
wallet: WalletTypeInfo = Depends(require_admin_key),
|
||||||
|
):
|
||||||
|
card = await get_card(card_id)
|
||||||
|
if not card:
|
||||||
|
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
|
||||||
|
)
|
||||||
|
card = await enable_disable_card(enable=enable, id=card_id)
|
||||||
|
logger.debug(enable)
|
||||||
|
logger.debug(card)
|
||||||
|
return card.dict()
|
||||||
|
|
||||||
@boltcards_ext.delete("/api/v1/cards/{card_id}")
|
@boltcards_ext.delete("/api/v1/cards/{card_id}")
|
||||||
async def api_card_delete(card_id, wallet: WalletTypeInfo = Depends(require_admin_key)):
|
async def api_card_delete(card_id, wallet: WalletTypeInfo = Depends(require_admin_key)):
|
||||||
|
|
Loading…
Add table
Reference in a new issue