extension loading

This commit is contained in:
benarc 2022-02-07 20:01:01 +00:00 committed by Tiago vasconcelos
parent 61cf79de69
commit d60d5c928c
10 changed files with 204 additions and 852 deletions

View file

@ -11,7 +11,5 @@ nostr_ext: APIRouter = APIRouter(prefix="/nostr", tags=["nostr"])
def nostr_renderer(): def nostr_renderer():
return template_renderer(["lnbits/extensions/nostr/templates"]) return template_renderer(["lnbits/extensions/nostr/templates"])
from .lnurl import * # noqa
from .views import * # noqa from .views import * # noqa
from .views_api import * # noqa from .views_api import * # noqa

View file

@ -1,33 +1,31 @@
from typing import List, Optional, Union from typing import List, Optional, Union
from lnbits.helpers import urlsafe_short_hash from lnbits.helpers import urlsafe_short_hash
import shortuuid
from . import db from . import db
from .models import nostrKeys, nostrNotes, nostrRelays, nostrConnections from .models import nostrKeys, nostrNotes, nostrCreateRelays, nostrRelays, nostrConnections, nostrCreateConnections
###############KEYS################## ###############KEYS##################
async def create_nostrkeys( async def create_nostrkeys(
data: nostrKeys data: nostrKeys
) -> nostrKeys: ) -> nostrKeys:
nostrkey_id = urlsafe_short_hash()
await db.execute( await db.execute(
""" """
INSERT INTO nostr.keys ( INSERT INTO nostr.keys (
id,
pubkey, pubkey,
privkey privkey
) )
VALUES (?, ?, ?) VALUES (?, ?)
""", """,
(nostrkey_id, data.pubkey, data.privkey), (data.pubkey, data.privkey),
) )
return await get_nostrkeys(nostrkey_id) return await get_nostrkeys(nostrkey_id)
async def get_nostrkeys(nostrkey_id: str) -> nostrKeys: async def get_nostrkeys(pubkey: str) -> nostrKeys:
row = await db.fetchone( row = await db.fetchone(
"SELECT * FROM nostr.keys WHERE id = ?", "SELECT * FROM nostr.keys WHERE pubkey = ?",
(lnurldevicepayment_id,), (pubkey,),
) )
return nostrKeys(**row) if row else None return nostrKeys(**row) if row else None
@ -64,9 +62,12 @@ async def get_nostrnotes(nostrnote_id: str) -> nostrNotes:
###############RELAYS################## ###############RELAYS##################
async def create_nostrrelays( async def create_nostrrelays(
relay: str data: nostrCreateRelays
) -> nostrRelays: ) -> nostrCreateRelays:
nostrrelay_id = urlsafe_short_hash() nostrrelay_id = shortuuid.uuid(name=relay)
if await get_nostrrelays(nostrrelay_id):
return "error"
await db.execute( await db.execute(
""" """
INSERT INTO nostr.relays ( INSERT INTO nostr.relays (
@ -75,7 +76,7 @@ async def create_nostrrelays(
) )
VALUES (?, ?) VALUES (?, ?)
""", """,
(nostrrelay_id, relay), (nostrrelay_id, data.relay),
) )
return await get_nostrnotes(nostrrelay_id) return await get_nostrnotes(nostrrelay_id)
@ -90,45 +91,25 @@ async def get_nostrrelays(nostrrelay_id: str) -> nostrRelays:
###############CONNECTIONS################## ###############CONNECTIONS##################
async def create_nostrconnections( async def create_nostrconnections(
data: nostrNotes data: nostrCreateConnections
) -> nostrNotes: ) -> nostrCreateConnections:
nostrkey_id = urlsafe_short_hash() nostrrelay_id = shortuuid.uuid(name=data.relayid + data.pubkey)
await db.execute( await db.execute(
""" """
INSERT INTO nostr.notes ( INSERT INTO nostr.connections (
id, id,
pubkey, pubkey,
created_at, relayid
kind,
tags,
content,
sig
) )
VALUES (?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?)
""", """,
(data.id, data.pubkey, data.created_at, data.kind, data.tags, data.content, data.sig), (data.id, data.pubkey, data.relayid),
) )
return await get_nostrnotes(data.id) return await get_nostrconnections(data.id)
async def get_nostrnotes(nostrnote_id: str) -> nostrNotes: async def get_nostrconnections(nostrconnections_id: str) -> nostrConnections:
row = await db.fetchone( row = await db.fetchone(
"SELECT * FROM nostr.notes WHERE id = ?", "SELECT * FROM nostr.connections WHERE id = ?",
(nostrnote_id,), (nostrconnections_id,),
) )
return nostrNotes(**row) if row else None return nostrConnections(**row) if row else None
async def update_lnurldevicepayment(
lnurldevicepayment_id: str, **kwargs
) -> Optional[lnurldevicepayment]:
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
await db.execute(
f"UPDATE lnurldevice.lnurldevicepayment SET {q} WHERE id = ?",
(*kwargs.values(), lnurldevicepayment_id),
)
row = await db.fetchone(
"SELECT * FROM lnurldevice.lnurldevicepayment WHERE id = ?",
(lnurldevicepayment_id,),
)
return lnurldevicepayment(**row) if row else None

View file

@ -24,7 +24,7 @@ async def m001_initial(db):
kind INT NOT NULL, kind INT NOT NULL,
tags TEXT NOT NULL, tags TEXT NOT NULL,
content TEXT NOT NULL, content TEXT NOT NULL,
sig TEXT NOT NULL, sig TEXT NOT NULL
); );
""" """
) )

View file

@ -3,15 +3,10 @@ from sqlite3 import Row
from typing import Optional from typing import Optional
from fastapi import Request from fastapi import Request
from lnurl import Lnurl
from lnurl import encode as lnurl_encode # type: ignore
from lnurl.models import LnurlPaySuccessAction, UrlAction # type: ignore
from lnurl.types import LnurlPayMetadata # type: ignore
from pydantic import BaseModel from pydantic import BaseModel
from pydantic.main import BaseModel from pydantic.main import BaseModel
class nostrKeys(BaseModel): class nostrKeys(BaseModel):
id: str
pubkey: str pubkey: str
privkey: str privkey: str
@ -24,6 +19,13 @@ class nostrNotes(BaseModel):
content: str content: str
sig: str sig: str
class nostrCreateRelays(BaseModel):
relay: str
class nostrCreateConnections(BaseModel):
pubkey: str
relayid: str
class nostrRelays(BaseModel): class nostrRelays(BaseModel):
id: str id: str
relay: str relay: str

View file

@ -1,169 +0,0 @@
<q-card>
<q-card-section>
<p>
Register LNURLDevice devices to receive payments in your LNbits wallet.<br />
Build your own here
<a href="https://github.com/arcbtc/bitcoinpos"
>https://github.com/arcbtc/bitcoinpos</a
><br />
<small>
Created by, <a href="https://github.com/benarc">Ben Arc</a></small
>
</p>
</q-card-section>
<q-expansion-item
group="extras"
icon="swap_vertical_circle"
label="API info"
:content-inset-level="0.5"
>
<q-expansion-item
group="api"
dense
expand-separator
label="Create lnurldevice"
>
<q-card>
<q-card-section>
<code
><span class="text-blue"></span> /lnurldevice/api/v1/lnurlpos</code
>
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
<code>{"X-Api-Key": &lt;admin_key&gt;}</code><br />
<h5 class="text-caption q-mt-sm q-mb-none">
Body (application/json)
</h5>
<h5 class="text-caption q-mt-sm q-mb-none">
Returns 200 OK (application/json)
</h5>
<code>[&lt;lnurldevice_object&gt;, ...]</code>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X POST {{ request.base_url }}api/v1/lnurldevice -d '{"title":
&lt;string&gt;, "message":&lt;string&gt;, "currency":
&lt;integer&gt;}' -H "Content-type: application/json" -H "X-Api-Key:
{{user.wallets[0].adminkey }}"
</code>
</q-card-section>
</q-card>
</q-expansion-item>
<q-expansion-item
group="api"
dense
expand-separator
label="Update lnurldevice"
>
<q-card>
<q-card-section>
<code
><span class="text-blue">PUT</span>
/lnurldevice/api/v1/lnurlpos/&lt;lnurldevice_id&gt;</code
>
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
<code>{"X-Api-Key": &lt;admin_key&gt;}</code><br />
<h5 class="text-caption q-mt-sm q-mb-none">
Body (application/json)
</h5>
<h5 class="text-caption q-mt-sm q-mb-none">
Returns 200 OK (application/json)
</h5>
<code>[&lt;lnurldevice_object&gt;, ...]</code>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X POST {{ request.base_url
}}api/v1/lnurlpos/&lt;lnurldevice_id&gt; -d ''{"title":
&lt;string&gt;, "message":&lt;string&gt;, "currency":
&lt;integer&gt;} -H "Content-type: application/json" -H "X-Api-Key:
{{user.wallets[0].adminkey }}"
</code>
</q-card-section>
</q-card>
</q-expansion-item>
<q-expansion-item
group="api"
dense
expand-separator
label="Get lnurldevice"
>
<q-card>
<q-card-section>
<code
><span class="text-blue">GET</span>
/lnurldevice/api/v1/lnurlpos/&lt;lnurldevice_id&gt;</code
>
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
<code>{"X-Api-Key": &lt;invoice_key&gt;}</code><br />
<h5 class="text-caption q-mt-sm q-mb-none">
Body (application/json)
</h5>
<h5 class="text-caption q-mt-sm q-mb-none">
Returns 200 OK (application/json)
</h5>
<code>[&lt;lnurldevice_object&gt;, ...]</code>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X GET {{ request.base_url
}}api/v1/lnurlpos/&lt;lnurldevice_id&gt; -H "X-Api-Key: {{
user.wallets[0].inkey }}"
</code>
</q-card-section>
</q-card>
</q-expansion-item>
<q-expansion-item
group="api"
dense
expand-separator
label="Get lnurldevices"
>
<q-card>
<q-card-section>
<code
><span class="text-blue">GET</span>
/lnurldevice/api/v1/lnurlposs</code
>
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
<code>{"X-Api-Key": &lt;invoice_key&gt;}</code><br />
<h5 class="text-caption q-mt-sm q-mb-none">
Body (application/json)
</h5>
<h5 class="text-caption q-mt-sm q-mb-none">
Returns 200 OK (application/json)
</h5>
<code>[&lt;lnurldevice_object&gt;, ...]</code>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X GET {{ request.base_url }}api/v1/lnurldevices -H
"X-Api-Key: {{ user.wallets[0].inkey }}"
</code>
</q-card-section>
</q-card>
</q-expansion-item>
<q-expansion-item
group="api"
dense
expand-separator
label="Delete a pay link"
class="q-pb-md"
>
<q-card>
<q-card-section>
<code
><span class="text-pink">DELETE</span>
/lnurldevice/api/v1/lnurlpos/&lt;lnurldevice_id&gt;</code
>
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
<code>{"X-Api-Key": &lt;admin_key&gt;}</code><br />
<h5 class="text-caption q-mt-sm q-mb-none">Returns 204 NO CONTENT</h5>
<code></code>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X DELETE {{ request.base_url
}}api/v1/lnurlpos/&lt;lnurldevice_id&gt; -H "X-Api-Key: {{
user.wallets[0].adminkey }}"
</code>
</q-card-section>
</q-card>
</q-expansion-item>
</q-expansion-item>
</q-card>

View file

@ -1,34 +0,0 @@
{% extends "public.html" %} {% block page %}
<div class="row q-col-gutter-md justify-center">
<div class="col-12 col-md-7 col-lg-6 q-gutter-y-md">
<q-card class="q-pa-lg">
<q-card-section class="q-pa-none">
<center>
<h3 class="q-my-none">LNURL-pay not paid</h3>
<br />
<q-icon
name="warning"
class="text-grey"
style="font-size: 20rem"
></q-icon>
<br />
</center>
</q-card-section>
</q-card>
</div>
{% endblock %} {% block scripts %}
<script>
new Vue({
el: '#vue',
mixins: [windowMixin],
data: function () {
return {}
}
})
</script>
{% endblock %}
</div>

View file

@ -1,534 +0,0 @@
{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
%} {% block page %}
<div class="row q-col-gutter-md">
<div class="col-12 col-md-7 q-gutter-y-md">
<q-card>
<q-card-section>
{% raw %}
<q-btn
unelevated
color="primary"
@click="formDialoglnurldevice.show = true"
>New LNURLDevice instance
</q-btn>
</q-card-section>
</q-card>
<q-card>
<q-card-section>
<div class="row items-center no-wrap q-mb-md">
<div class="col">
<h5 class="text-subtitle1 q-my-none">lNURLdevice</h5>
</div>
<div class="col-auto">
<q-input
borderless
dense
debounce="300"
v-model="filter"
placeholder="Search"
>
<template v-slot:append>
<q-icon name="search"></q-icon>
</template>
</q-input>
<q-btn flat color="grey" @click="exportlnurldeviceCSV"
>Export to CSV</q-btn
>
</div>
</div>
<q-table
flat
dense
:data="lnurldeviceLinks"
row-key="id"
:columns="lnurldevicesTable.columns"
:pagination.sync="lnurldevicesTable.pagination"
:filter="filter"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th style="width: 5%"></q-th>
<q-th style="width: 5%"></q-th>
<q-th
v-for="col in props.cols"
:key="col.name"
:props="props"
auto-width
>
<div v-if="col.name == 'id'"></div>
<div v-else>{{ col.label }}</div>
</q-th>
<!-- <q-th auto-width></q-th> -->
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props">
<q-td>
<q-btn
flat
dense
size="xs"
@click="deletelnurldeviceLink(props.row.id)"
icon="cancel"
color="pink"
>
<q-tooltip> Delete LNURLDevice </q-tooltip>
</q-btn>
</q-td>
<q-td>
<q-btn
flat
dense
size="xs"
@click="openlnurldeviceSettings(props.row.id)"
icon="perm_data_setting"
color="primary"
>
<q-tooltip> LNURLDevice Settings </q-tooltip>
</q-btn>
</q-td>
<q-td
v-for="col in props.cols"
:key="col.name"
:props="props"
auto-width
>
<div v-if="col.name == 'id'"></div>
<div v-else>{{ col.value }}</div>
</q-td>
</q-tr>
</template>
{% endraw %}
</q-table>
</q-card-section>
</q-card>
</div>
<div class="col-12 col-md-5 q-gutter-y-md">
<q-card>
<q-card-section>
<h6 class="text-subtitle1 q-my-none">
{{SITE_TITLE}} LNURLDevice Extension
</h6>
</q-card-section>
<q-card-section class="q-pa-none">
<q-separator></q-separator>
<q-list> {% include "lnurldevice/_api_docs.html" %} </q-list>
</q-card-section>
</q-card>
</div>
<q-dialog
v-model="settingsDialog.show"
deviceition="top"
@hide="closeFormDialog"
>
<q-card
style="width: 700px; max-width: 80vw"
class="q-pa-lg q-pt-xl lnbits__dialog-card"
>
<div class="text-h6">LNURLDevice device string</div>
<q-btn
dense
outline
unelevated
color="primary"
size="md"
@click="copyText(location + '/lnurldevice/api/v1/lnurl/' + settingsDialog.data.id + ',' +
settingsDialog.data.key + ',' + settingsDialog.data.currency, 'Link copied to clipboard!')"
>{% raw
%}{{location}}/lnurldevice/api/v1/lnurl/{{settingsDialog.data.id}},
{{settingsDialog.data.key}}, {{settingsDialog.data.currency}}{% endraw
%}<q-tooltip> Click to copy URL </q-tooltip>
</q-btn>
<div class="text-subtitle2">
<small> </small>
</div>
</q-card>
</q-dialog>
<q-dialog
v-model="formDialoglnurldevice.show"
deviceition="top"
@hide="closeFormDialog"
>
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
<q-form @submit="sendFormDatalnurldevice" class="q-gutter-md">
<q-input
filled
dense
v-model.trim="formDialoglnurldevice.data.title"
type="text"
label="Title"
></q-input>
<q-select
filled
dense
emit-value
v-model="formDialoglnurldevice.data.wallet"
:options="g.user.walletOptions"
label="Wallet *"
></q-select>
<q-select
filled
dense
v-model.trim="formDialoglnurldevice.data.currency"
type="text"
label="Fiat currency for device"
:options="currency"
></q-select>
<q-option-group
v-model.trim="formDialoglnurldevice.data.device"
:options="devices"
color="primary"
label="Type of device"
></q-option-group>
<q-input
filled
dense
v-model.trim="formDialoglnurldevice.data.profit"
type="number"
max="90"
label="Profit margin (% added to invoices/deducted from faucets)"
></q-input>
<div class="row q-mt-lg">
<q-btn
v-if="formDialoglnurldevice.data.id"
unelevated
color="primary"
:disable="
formDialoglnurldevice.data.title == ''"
type="submit"
>Update lnurldevice</q-btn
>
<q-btn
v-else
unelevated
color="primary"
:disable="
formDialoglnurldevice.data.title == ''"
type="submit"
>Create lnurldevice</q-btn
>
<q-btn @click="cancellnurldevice" flat color="grey" class="q-ml-auto"
>Cancel</q-btn
>
</div>
</q-form>
</q-card>
</q-dialog>
</div>
{% endblock %} {% block scripts %} {{ window_vars(user) }}
<script>
Vue.component(VueQrcode.name, VueQrcode)
var maplnurldevice = obj => {
obj._data = _.clone(obj)
obj.theTime = obj.time * 60 - (Date.now() / 1000 - obj.timestamp)
obj.time = obj.time + 'mins'
if (obj.time_elapsed) {
obj.date = 'Time elapsed'
} else {
obj.date = Quasar.utils.date.formatDate(
new Date((obj.theTime - 3600) * 1000),
'HH:mm:ss'
)
}
return obj
}
new Vue({
el: '#vue',
mixins: [windowMixin],
data: function () {
return {
location: window.location.hostname,
filter: '',
currency: 'USD',
lnurldeviceLinks: [],
lnurldeviceLinksObj: [],
devices: [
{
label: 'PoS',
value: 'pos'
},
{
label: 'ATM',
value: 'atm'
}
],
lnurldevicesTable: {
columns: [
{
name: 'title',
align: 'left',
label: 'title',
field: 'title'
},
{
name: 'theId',
align: 'left',
label: 'id',
field: 'id'
},
{
name: 'key',
align: 'left',
label: 'key',
field: 'key'
},
{
name: 'wallet',
align: 'left',
label: 'wallet',
field: 'wallet'
},
{
name: 'device',
align: 'left',
label: 'device',
field: 'device'
},
{
name: 'profit',
align: 'left',
label: 'profit',
field: 'profit'
},
{
name: 'currency',
align: 'left',
label: 'currency',
field: 'currency'
}
],
pagination: {
rowsPerPage: 10
}
},
passedlnurldevice: {},
settingsDialog: {
show: false,
data: {}
},
formDialog: {
show: false,
data: {}
},
formDialoglnurldevice: {
show: false,
data: {
lnurl_toggle: false,
show_message: false,
show_ack: false,
show_price: 'None',
device: 'pos',
profit: 2,
title: ''
}
},
qrCodeDialog: {
show: false,
data: null
}
}
},
methods: {
cancellnurldevice: function (data) {
var self = this
self.formDialoglnurldevice.show = false
self.clearFormDialoglnurldevice()
},
closeFormDialog: function () {
this.clearFormDialoglnurldevice()
this.formDialog.data = {
is_unique: false
}
},
sendFormDatalnurldevice: function () {
var self = this
if (self.formDialoglnurldevice.data.id) {
this.updatelnurldevice(
self.g.user.wallets[0].adminkey,
self.formDialoglnurldevice.data
)
} else {
this.createlnurldevice(
self.g.user.wallets[0].adminkey,
self.formDialoglnurldevice.data
)
}
},
createlnurldevice: function (wallet, data) {
var self = this
var updatedData = {}
for (const property in data) {
if (data[property]) {
updatedData[property] = data[property]
}
}
LNbits.api
.request('POST', '/lnurldevice/api/v1/lnurlpos', wallet, updatedData)
.then(function (response) {
self.lnurldeviceLinks.push(maplnurldevice(response.data))
self.formDialoglnurldevice.show = false
self.clearFormDialoglnurldevice()
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
},
getlnurldevices: function () {
var self = this
LNbits.api
.request(
'GET',
'/lnurldevice/api/v1/lnurlpos',
self.g.user.wallets[0].adminkey
)
.then(function (response) {
if (response.data) {
self.lnurldeviceLinks = response.data.map(maplnurldevice)
}
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
},
getlnurldevice: function (lnurldevice_id) {
var self = this
LNbits.api
.request(
'GET',
'/lnurldevice/api/v1/lnurlpos/' + lnurldevice_id,
self.g.user.wallets[0].adminkey
)
.then(function (response) {
localStorage.setItem('lnurldevice', JSON.stringify(response.data))
localStorage.setItem('inkey', self.g.user.wallets[0].inkey)
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
},
deletelnurldeviceLink: function (lnurldeviceId) {
var self = this
var link = _.findWhere(this.lnurldeviceLinks, {id: lnurldeviceId})
LNbits.utils
.confirmDialog('Are you sure you want to delete this pay link?')
.onOk(function () {
LNbits.api
.request(
'DELETE',
'/lnurldevice/api/v1/lnurlpos/' + lnurldeviceId,
self.g.user.wallets[0].adminkey
)
.then(function (response) {
self.lnurldeviceLinks = _.reject(
self.lnurldeviceLinks,
function (obj) {
return obj.id === lnurldeviceId
}
)
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
})
},
openUpdatelnurldeviceLink: function (lnurldeviceId) {
var self = this
var lnurldevice = _.findWhere(this.lnurldeviceLinks, {
id: lnurldeviceId
})
self.formDialoglnurldevice.data = _.clone(lnurldevice._data)
self.formDialoglnurldevice.show = true
},
openlnurldeviceSettings: function (lnurldeviceId) {
var self = this
var lnurldevice = _.findWhere(this.lnurldeviceLinks, {
id: lnurldeviceId
})
self.settingsDialog.data = _.clone(lnurldevice._data)
self.settingsDialog.show = true
},
updatelnurldevice: function (wallet, data) {
var self = this
var updatedData = {}
for (const property in data) {
if (data[property]) {
updatedData[property] = data[property]
}
}
LNbits.api
.request(
'PUT',
'/lnurldevice/api/v1/lnurlpos/' + updatedData.id,
wallet,
updatedData
)
.then(function (response) {
self.lnurldeviceLinks = _.reject(
self.lnurldeviceLinks,
function (obj) {
return obj.id === updatedData.id
}
)
self.lnurldeviceLinks.push(maplnurldevice(response.data))
self.formDialoglnurldevice.show = false
self.clearFormDialoglnurldevice()
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
},
clearFormDialoglnurldevice() {
this.formDialoglnurldevice.data = {
lnurl_toggle: false,
show_message: false,
show_ack: false,
show_price: 'None',
title: ''
}
},
exportlnurldeviceCSV: function () {
var self = this
LNbits.utils.exportCSV(
self.lnurldevicesTable.columns,
this.lnurldeviceLinks
)
}
},
created: function () {
var self = this
var getlnurldevices = this.getlnurldevices
getlnurldevices()
self.location = [
window.location.protocol,
'//',
window.location.host
].join('')
LNbits.api
.request('GET', '/api/v1/currencies')
.then(response => {
this.currency = ['USD', ...response.data]
})
.catch(err => {
LNbits.utils.notifyApiError(err)
})
}
})
</script>
{% endblock %}

View file

@ -1,27 +0,0 @@
{% extends "public.html" %} {% block page %}
<div class="row q-col-gutter-md justify-center">
<div class="col-12 col-md-7 col-lg-6 q-gutter-y-md">
<q-card class="q-pa-lg">
<q-card-section class="q-pa-none">
<center>
<h3 class="q-my-none">{{ pin }}</h3>
<br />
</center>
</q-card-section>
</q-card>
</div>
{% endblock %} {% block scripts %}
<script>
new Vue({
el: '#vue',
mixins: [windowMixin],
data: function () {
return {}
}
})
</script>
{% endblock %}
</div>

View file

@ -0,0 +1,159 @@
{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
%} {% block page %}
<div class="row q-col-gutter-md">
<div class="col-12 col-md-7 q-gutter-y-md">
<q-card>
<q-card-section>
<div class="row items-center no-wrap q-mb-md">
<div class="col">
<h5 class="text-subtitle1 q-my-none">Nostr</h5>
</div>
<div class="col-auto">
<q-input
borderless
dense
debounce="300"
v-model="filter"
placeholder="Search"
>
<template v-slot:append>
<q-icon name="search"></q-icon>
</template>
</q-input>
<q-btn flat color="grey" @click="exportlnurldeviceCSV"
>Export to CSV</q-btn
>
</div>
</div>
<q-table
flat
dense
:data="lnurldeviceLinks"
row-key="id"
:columns="nostrTable.columns"
:pagination.sync="nostrTable.pagination"
:filter="filter"
>
{% raw %}
<template v-slot:header="props">
<q-tr :props="props">
<q-th style="width: 5%"></q-th>
<q-th style="width: 5%"></q-th>
<q-th
v-for="col in props.cols"
:key="col.name"
:props="props"
auto-width
>
<div v-if="col.name == 'id'"></div>
<div v-else>{{ col.label }}</div>
</q-th>
<!-- <q-th auto-width></q-th> -->
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props">
<q-td
v-for="col in props.cols"
:key="col.name"
:props="props"
auto-width
>
<div v-if="col.name == 'id'"></div>
<div v-else>{{ col.value }}</div>
</q-td>
</q-tr>
</template>
{% endraw %}
</q-table>
</q-card-section>
</q-card>
</div>
<div class="col-12 col-md-5 q-gutter-y-md">
<q-card>
<q-card-section>
<h6 class="text-subtitle1 q-my-none">{{SITE_TITLE}} Nostr Extension</h6>
<q-card-section>Okay </q-card-section>
</q-card-section>
</q-card>
</div>
</div>
{% endblock %} {% block scripts %} {{ window_vars(user) }}
<script>
Vue.component(VueQrcode.name, VueQrcode)
var maplnurldevice = obj => {
obj._data = _.clone(obj)
obj.theTime = obj.time * 60 - (Date.now() / 1000 - obj.timestamp)
obj.time = obj.time + 'mins'
if (obj.time_elapsed) {
obj.date = 'Time elapsed'
} else {
obj.date = Quasar.utils.date.formatDate(
new Date((obj.theTime - 3600) * 1000),
'HH:mm:ss'
)
}
return obj
}
new Vue({
el: '#vue',
mixins: [windowMixin],
data: function () {
return {
nostrTable: {
columns: [
{
name: 'relay',
align: 'left',
label: 'relay',
field: 'relay'
},
{
name: 'connected',
align: 'left',
label: 'connected',
field: 'connected'
}
],
pagination: {
rowsPerPage: 10
}
}
}
},
methods: {
getrelays: function () {
var self = this
LNbits.api
.request(
'GET',
'/nostr/api/v1/relays',
self.g.user.wallets[0].adminkey
)
.then(function (response) {
if (response.data) {
self.lnurldeviceLinks = response.data.map(maplnurldevice)
}
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
}
},
created: function () {
var self = this
var getrelays = this.getrelays
getrelays()
}
})
</script>
{% endblock %}

View file

@ -7,42 +7,18 @@ from starlette.exceptions import HTTPException
from lnbits.core.crud import get_user from lnbits.core.crud import get_user
from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
from lnbits.extensions.lnurldevice import lnurldevice_ext from lnbits.extensions.nostr import nostr_ext
from lnbits.utils.exchange_rates import currencies from lnbits.utils.exchange_rates import currencies
from . import lnurldevice_ext from . import nostr_ext
from .crud import ( from .crud import (
create_lnurldevice, create_nostrkeys,
delete_lnurldevice, get_nostrkeys,
get_lnurldevice, create_nostrnotes,
get_lnurldevices, get_nostrnotes,
update_lnurldevice, create_nostrrelays,
get_nostrrelays,
create_nostrconnections,
get_nostrconnections,
) )
from .models import createLnurldevice from .models import nostrKeys
@nostr_ext.get("/api/v1/lnurlpos")
async def api_check_daemon(wallet: WalletTypeInfo = Depends(get_key_type)):
wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
try:
return [
{**lnurldevice.dict()} for lnurldevice in await get_lnurldevices(wallet_ids)
]
except:
return ""
@nostr_ext.delete("/api/v1/lnurlpos/{lnurldevice_id}")
async def api_lnurldevice_delete(
wallet: WalletTypeInfo = Depends(require_admin_key),
lnurldevice_id: str = Query(None),
):
lnurldevice = await get_lnurldevice(lnurldevice_id)
if not lnurldevice:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="Wallet link does not exist."
)
await delete_lnurldevice(lnurldevice_id)
return "", HTTPStatus.NO_CONTENT