feat: add payment for stoeage

This commit is contained in:
Vlad Stan 2023-02-13 17:49:10 +02:00
parent 2233521a43
commit dfda2367a2
5 changed files with 130 additions and 35 deletions

View file

@ -10,3 +10,11 @@ UI for diagnostics and management (key alow/ban lists, rate limiting) coming soo
1. Enable extension 1. Enable extension
2. Enable relay 2. Enable relay
### Development
Create Symbolic Link:
```
ln -s /Users/my-user/git-repos/nostr-relay-extension/ /Users/my-user/git-repos/lnbits/lnbits/extensions/nostrrelay
```

View file

@ -307,10 +307,14 @@ class NostrFilter(BaseModel):
return inner_joins, where, values return inner_joins, where, values
class RelayJoin(BaseModel): class BuyOrder(BaseModel):
action: str
relay_id: str relay_id: str
pubkey: str pubkey: str
units_to_buy = 0
def is_valid_action(self):
return self.action in ['join', 'storage']
class NostrAccount(BaseModel): class NostrAccount(BaseModel):
pubkey: str pubkey: str

View file

@ -31,6 +31,11 @@ async def on_invoice_paid(payment: Payment):
await invoice_paid_to_join(relay_id, pubkey) await invoice_paid_to_join(relay_id, pubkey)
return return
if payment.extra.get("action") == "storage":
storage_to_buy = payment.extra.get("storage_to_buy")
await invoice_paid_for_storage(relay_id, pubkey, storage_to_buy)
return
async def invoice_paid_to_join(relay_id: str, pubkey: str): async def invoice_paid_to_join(relay_id: str, pubkey: str):
try: try:
account = await get_account(relay_id, pubkey) account = await get_account(relay_id, pubkey)
@ -44,5 +49,22 @@ async def invoice_paid_to_join(relay_id: str, pubkey: str):
account.paid_to_join = True account.paid_to_join = True
await update_account(relay_id, account) await update_account(relay_id, account)
except Exception as ex:
logger.warning(ex)
async def invoice_paid_for_storage(relay_id: str, pubkey: str, storage_to_buy: int):
try:
account = await get_account(relay_id, pubkey)
if not account:
await create_account(relay_id, NostrAccount(pubkey=pubkey, storage=storage_to_buy))
return
if account.blocked:
return
account.storage = storage_to_buy
await update_account(relay_id, account)
except Exception as ex: except Exception as ex:
logger.warning(ex) logger.warning(ex)

View file

@ -12,7 +12,7 @@
<h4 v-text="relay.name" class="q-my-none"></h4> <h4 v-text="relay.name" class="q-my-none"></h4>
</q-card-section> </q-card-section>
</q-card> </q-card>
<q-card> <q-card class="q-pb-xl">
<q-card-section> <q-card-section>
<span class="text-bold">Public Key:</span> <span class="text-bold">Public Key:</span>
<q-input <q-input
@ -24,38 +24,78 @@
></q-input> ></q-input>
</q-card-section> </q-card-section>
<q-card-section v-if="relay.config.isPaidRelay"> <q-card-section v-if="relay.config.isPaidRelay">
<q-btn <div class="row q-mb-md">
@click="createJoinInvoice" <div class="col-2">
unelevated <span class="text-bold">Cost to join: </span>
color="primary float-right" </div>
>Show Invoice</q-btn <div class="col-6">
> <span v-text="relay.config.costToJoin"></span>
<span class="text-bold">Cost to join: </span> <span class="text-bold q-ml-sm">sats</span>
</div>
<span v-text="relay.config.costToJoin"></span> <div class="col-4">
<q-badge color="orange"> <q-btn
<span>sats</span> @click="createInvoice('join')"
</q-badge> unelevated
color="primary"
class="float-right"
>Pay to Join</q-btn
>
</div>
</div>
<q-separator></q-separator>
<div class="row q-mt-md q-mb-md">
<div class="col-2">
<span class="text-bold">Storage cost: </span>
</div>
<div class="col-3">
<span v-text="relay.config.storageCostValue"></span>
<span class="text-bold q-ml-sm"> sats per</span>
<q-badge color="orange">
<span v-text="relay.config.storageCostUnit"></span>
</q-badge>
</div>
<div class="col-2">
<q-input
filled
dense
v-model="unitsToBuy"
type="number"
min="0"
:label="relay.config.storageCostUnit"
></q-input>
</div>
<div class="col-2">
<span class="text-bold q-ml-md" v-text="storageCost"></span>
<span>sats</span>
</div>
<div class="col-3">
<q-btn
@click="createInvoice('storage')"
unelevated
color="primary"
class="float-right"
>Buy storage space</q-btn
>
</div>
</div>
<q-separator></q-separator>
</q-card-section> </q-card-section>
<q-card-section v-else> <q-card-section v-else>
<q-badge color="yellow" text-color="black"> <q-badge color="yellow" text-color="black">
This is a free relay This is a free relay
</q-badge> </q-badge>
</q-card-section> </q-card-section>
<q-card-section v-if="joinInvoice"> <q-card-section v-if="invoice">
<q-expansion-item <q-expansion-item
group="join-invoice" group="join-invoice"
label="Pay invoice to join relay" label="Invoice"
:content-inset-level="0.5" :content-inset-level="0.5"
default-opened default-opened
> >
<div class="row q-ma-md"> <div class="row q-ma-md">
<div class="col-3"></div> <div class="col-3"></div>
<div class="col-6 text-center"> <div class="col-6 text-center">
<q-btn <q-btn outline color="grey" @click="copyText(invoice)"
outline
color="grey"
@click="copyText(joinInvoice)"
>Copy invoice</q-btn >Copy invoice</q-btn
> >
</div> </div>
@ -66,7 +106,7 @@
<div class="col-6"> <div class="col-6">
<q-responsive :ratio="1"> <q-responsive :ratio="1">
<qrcode <qrcode
:value="'lightning:'+joinInvoice" :value="'lightning:'+invoice"
:options="{width: 340}" :options="{width: 340}"
class="rounded-borders" class="rounded-borders"
></qrcode> ></qrcode>
@ -163,11 +203,19 @@
return { return {
relay: JSON.parse('{{relay | tojson | safe}}'), relay: JSON.parse('{{relay | tojson | safe}}'),
pubkey: '', pubkey: '',
joinInvoice: '' invoice: '',
unitsToBuy: 0
}
},
computed: {
storageCost: function () {
if (!this.relay || !this.relay.config.storageCostValue) return 0
return this.unitsToBuy * this.relay.config.storageCostValue
} }
}, },
methods: { methods: {
createJoinInvoice: async function () { createInvoice: async function (action) {
if (!action) return
if (!this.pubkey) { if (!this.pubkey) {
this.$q.notify({ this.$q.notify({
timeout: 5000, timeout: 5000,
@ -177,17 +225,20 @@
return return
} }
try { try {
const reqData = {
action,
relay_id: this.relay.id,
pubkey: this.pubkey,
units_to_buy: this.unitsToBuy
}
const {data} = await LNbits.api.request( const {data} = await LNbits.api.request(
'PUT', 'PUT',
'/nostrrelay/api/v1/join', '/nostrrelay/api/v1/pay',
'', '',
{ reqData
relay_id: this.relay.id,
pubkey: this.pubkey
}
) )
console.log('### data.invoice', data.invoice) console.log('### data.invoice', data.invoice)
this.joinInvoice = data.invoice this.invoice = data.invoice
} catch (error) { } catch (error) {
LNbits.utils.notifyApiError(error) LNbits.utils.notifyApiError(error)
} }

View file

@ -27,7 +27,7 @@ from .crud import (
update_relay, update_relay,
) )
from .helpers import normalize_public_key from .helpers import normalize_public_key
from .models import NostrRelay, RelayJoin from .models import BuyOrder, NostrRelay
client_manager = NostrClientManager() client_manager = NostrClientManager()
@ -154,9 +154,8 @@ async def api_delete_relay(
) )
@nostrrelay_ext.put("/api/v1/join") @nostrrelay_ext.put("/api/v1/pay")
async def api_pay_to_join(data: RelayJoin): async def api_pay_to_join(data: BuyOrder):
try: try:
pubkey = normalize_public_key(data.pubkey) pubkey = normalize_public_key(data.pubkey)
relay = await get_relay_by_id(data.relay_id) relay = await get_relay_by_id(data.relay_id)
@ -166,18 +165,29 @@ async def api_pay_to_join(data: RelayJoin):
detail="Relay not found", detail="Relay not found",
) )
if relay.is_free_to_join: if data.action == 'join' and relay.is_free_to_join:
raise ValueError("Relay is free to join") raise ValueError("Relay is free to join")
storage_to_buy = 0
if data.action == 'storage':
if relay.config.storage_cost_value == 0:
raise ValueError("Relay storage cost is zero. Cannot buy!")
if data.units_to_buy == 0:
raise ValueError("Must specify how much storage to buy!")
storage_to_buy = data.units_to_buy * relay.config.storage_cost_value * 1024
if relay.config.storage_cost_unit == "MB":
storage_to_buy *= 1024
_, payment_request = await create_invoice( _, payment_request = await create_invoice(
wallet_id=relay.config.wallet, wallet_id=relay.config.wallet,
amount=int(relay.config.cost_to_join), amount=int(relay.config.cost_to_join),
memo=f"Pubkey '{data.pubkey}' wants to join {relay.id}", memo=f"Pubkey '{data.pubkey}' wants to join {relay.id}",
extra={ extra={
"tag": "nostrrely", "tag": "nostrrely",
"action": "join", "action": data.action,
"relay_id": relay.id, "relay_id": relay.id,
"pubkey": pubkey, "pubkey": pubkey,
"storage_to_buy": storage_to_buy
}, },
) )
print("### payment_request", payment_request) print("### payment_request", payment_request)