Added nostr ext

This commit is contained in:
benarc 2022-02-07 13:22:43 +00:00 committed by Tiago vasconcelos
parent 671ac08a04
commit 61cf79de69
12 changed files with 1077 additions and 0 deletions

View file

@ -0,0 +1,3 @@
# Nostr
Opens a Nostr daemon

View file

@ -0,0 +1,17 @@
from fastapi import APIRouter
from lnbits.db import Database
from lnbits.helpers import template_renderer
db = Database("ext_nostr")
nostr_ext: APIRouter = APIRouter(prefix="/nostr", tags=["nostr"])
def nostr_renderer():
return template_renderer(["lnbits/extensions/nostr/templates"])
from .lnurl import * # noqa
from .views import * # noqa
from .views_api import * # noqa

View file

@ -0,0 +1,6 @@
{
"name": "Nostr",
"short_description": "Daemon for Nostr",
"icon": "swap_horizontal_circle",
"contributors": ["arcbtc"]
}

View file

@ -0,0 +1,134 @@
from typing import List, Optional, Union
from lnbits.helpers import urlsafe_short_hash
from . import db
from .models import nostrKeys, nostrNotes, nostrRelays, nostrConnections
###############KEYS##################
async def create_nostrkeys(
data: nostrKeys
) -> nostrKeys:
nostrkey_id = urlsafe_short_hash()
await db.execute(
"""
INSERT INTO nostr.keys (
id,
pubkey,
privkey
)
VALUES (?, ?, ?)
""",
(nostrkey_id, data.pubkey, data.privkey),
)
return await get_nostrkeys(nostrkey_id)
async def get_nostrkeys(nostrkey_id: str) -> nostrKeys:
row = await db.fetchone(
"SELECT * FROM nostr.keys WHERE id = ?",
(lnurldevicepayment_id,),
)
return nostrKeys(**row) if row else None
###############NOTES##################
async def create_nostrnotes(
data: nostrNotes
) -> nostrNotes:
await db.execute(
"""
INSERT INTO nostr.notes (
id,
pubkey,
created_at,
kind,
tags,
content,
sig
)
VALUES (?, ?, ?, ?, ?, ?, ?)
""",
(data.id, data.pubkey, data.created_at, data.kind, data.tags, data.content, data.sig),
)
return await get_nostrnotes(data.id)
async def get_nostrnotes(nostrnote_id: str) -> nostrNotes:
row = await db.fetchone(
"SELECT * FROM nostr.notes WHERE id = ?",
(nostrnote_id,),
)
return nostrNotes(**row) if row else None
###############RELAYS##################
async def create_nostrrelays(
relay: str
) -> nostrRelays:
nostrrelay_id = urlsafe_short_hash()
await db.execute(
"""
INSERT INTO nostr.relays (
id,
relay
)
VALUES (?, ?)
""",
(nostrrelay_id, relay),
)
return await get_nostrnotes(nostrrelay_id)
async def get_nostrrelays(nostrrelay_id: str) -> nostrRelays:
row = await db.fetchone(
"SELECT * FROM nostr.relays WHERE id = ?",
(nostrnote_id,),
)
return nostrRelays(**row) if row else None
###############CONNECTIONS##################
async def create_nostrconnections(
data: nostrNotes
) -> nostrNotes:
nostrkey_id = urlsafe_short_hash()
await db.execute(
"""
INSERT INTO nostr.notes (
id,
pubkey,
created_at,
kind,
tags,
content,
sig
)
VALUES (?, ?, ?, ?, ?, ?, ?)
""",
(data.id, data.pubkey, data.created_at, data.kind, data.tags, data.content, data.sig),
)
return await get_nostrnotes(data.id)
async def get_nostrnotes(nostrnote_id: str) -> nostrNotes:
row = await db.fetchone(
"SELECT * FROM nostr.notes WHERE id = ?",
(nostrnote_id,),
)
return nostrNotes(**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

@ -0,0 +1,47 @@
from lnbits.db import Database
db2 = Database("ext_nostr")
async def m001_initial(db):
"""
Initial nostr table.
"""
await db.execute(
f"""
CREATE TABLE nostr.keys (
pubkey TEXT NOT NULL PRIMARY KEY,
privkey TEXT NOT NULL
);
"""
)
await db.execute(
f"""
CREATE TABLE nostr.notes (
id TEXT NOT NULL PRIMARY KEY,
pubkey TEXT NOT NULL,
created_at TEXT NOT NULL,
kind INT NOT NULL,
tags TEXT NOT NULL,
content TEXT NOT NULL,
sig TEXT NOT NULL,
);
"""
)
await db.execute(
f"""
CREATE TABLE nostr.relays (
id TEXT NOT NULL PRIMARY KEY,
relay TEXT NOT NULL
);
"""
)
await db.execute(
f"""
CREATE TABLE nostr.connections (
id TEXT NOT NULL PRIMARY KEY,
publickey TEXT NOT NULL,
relayid TEXT NOT NULL
);
"""
)

View file

@ -0,0 +1,34 @@
import json
from sqlite3 import Row
from typing import Optional
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.main import BaseModel
class nostrKeys(BaseModel):
id: str
pubkey: str
privkey: str
class nostrNotes(BaseModel):
id: str
pubkey: str
created_at: str
kind: int
tags: str
content: str
sig: str
class nostrRelays(BaseModel):
id: str
relay: str
class nostrConnections(BaseModel):
id: str
pubkey: str
relayid: str

View file

@ -0,0 +1,169 @@
<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

@ -0,0 +1,34 @@
{% 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

@ -0,0 +1,534 @@
{% 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

@ -0,0 +1,27 @@
{% 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,24 @@
from http import HTTPStatus
from fastapi import Request
from fastapi.param_functions import Query
from fastapi.params import Depends
from fastapi.templating import Jinja2Templates
from starlette.exceptions import HTTPException
from starlette.responses import HTMLResponse
from lnbits.core.crud import update_payment_status
from lnbits.core.models import User
from lnbits.core.views.api import api_payment
from lnbits.decorators import check_user_exists
from . import nostr_ext, nostr_renderer
templates = Jinja2Templates(directory="templates")
@nostr_ext.get("/", response_class=HTMLResponse)
async def index(request: Request, user: User = Depends(check_user_exists)):
return nostr_renderer().TemplateResponse(
"nostr/index.html", {"request": request, "user": user.dict()}
)

View file

@ -0,0 +1,48 @@
from http import HTTPStatus
from fastapi import Request
from fastapi.param_functions import Query
from fastapi.params import Depends
from starlette.exceptions import HTTPException
from lnbits.core.crud import get_user
from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
from lnbits.extensions.lnurldevice import lnurldevice_ext
from lnbits.utils.exchange_rates import currencies
from . import lnurldevice_ext
from .crud import (
create_lnurldevice,
delete_lnurldevice,
get_lnurldevice,
get_lnurldevices,
update_lnurldevice,
)
from .models import createLnurldevice
@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