mirror of
https://github.com/lnbits/lnbits.git
synced 2025-10-10 12:32:34 +02:00
Included currency conversion
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
|
||||||
@@ -22,10 +23,12 @@ example_static_files = [
|
|||||||
def example_renderer():
|
def example_renderer():
|
||||||
return template_renderer(["lnbits/extensions/example/templates"])
|
return template_renderer(["lnbits/extensions/example/templates"])
|
||||||
|
|
||||||
|
|
||||||
from .tasks import wait_for_paid_invoices
|
from .tasks import wait_for_paid_invoices
|
||||||
from .views import *
|
from .views import *
|
||||||
from .views_api import *
|
from .views_api import *
|
||||||
|
|
||||||
|
|
||||||
def tpos_start():
|
def tpos_start():
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
loop.create_task(catch_everything_and_restart(wait_for_paid_invoices))
|
loop.create_task(catch_everything_and_restart(wait_for_paid_invoices))
|
BIN
lnbits/extensions/example/static/conversion-example.png
Normal file
BIN
lnbits/extensions/example/static/conversion-example.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 77 KiB |
BIN
lnbits/extensions/example/static/conversion-example2.png
Normal file
BIN
lnbits/extensions/example/static/conversion-example2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
Binary file not shown.
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 9.6 KiB |
@@ -3,11 +3,14 @@
|
|||||||
# add your dependencies here
|
# add your dependencies here
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
from lnbits.core.models import Payment
|
from lnbits.core.models import Payment
|
||||||
from lnbits.helpers import get_current_extension_name
|
from lnbits.helpers import get_current_extension_name
|
||||||
from lnbits.tasks import register_invoice_listener
|
from lnbits.tasks import register_invoice_listener
|
||||||
|
|
||||||
|
|
||||||
async def wait_for_paid_invoices():
|
async def wait_for_paid_invoices():
|
||||||
invoice_queue = asyncio.Queue()
|
invoice_queue = asyncio.Queue()
|
||||||
register_invoice_listener(invoice_queue, get_current_extension_name())
|
register_invoice_listener(invoice_queue, get_current_extension_name())
|
||||||
@@ -16,8 +19,11 @@ async def wait_for_paid_invoices():
|
|||||||
payment = await invoice_queue.get()
|
payment = await invoice_queue.get()
|
||||||
await on_invoice_paid(payment)
|
await on_invoice_paid(payment)
|
||||||
|
|
||||||
|
|
||||||
async def on_invoice_paid(payment: Payment) -> None:
|
async def on_invoice_paid(payment: Payment) -> None:
|
||||||
if payment.extra.get("tag") != "example": # Will grab any payment with the tag "example"
|
if (
|
||||||
|
payment.extra.get("tag") != "example"
|
||||||
|
): # Will grab any payment with the tag "example"
|
||||||
logger.debug(payment)
|
logger.debug(payment)
|
||||||
# Do something
|
# Do something
|
||||||
return
|
return
|
@@ -4,12 +4,44 @@
|
|||||||
<q-dialog v-model="thingDialog.show" position="top">
|
<q-dialog v-model="thingDialog.show" position="top">
|
||||||
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||||
<q-form @submit="sendThingDialog" class="q-gutter-md">
|
<q-form @submit="sendThingDialog" class="q-gutter-md">
|
||||||
<q-input filled dense v-model.trim="thingDialog.data.name" type="text" label="Name *"></q-input>
|
<q-input
|
||||||
<q-input filled dense v-model.trim="thingDialog.data.email" type="email" label="Name *"></q-input>
|
filled
|
||||||
<q-select filled dense emit-value v-model="thingDialog.data.wallet" :options="g.user.walletOptions" label="Wallet *"></q-select>
|
dense
|
||||||
|
v-model.trim="thingDialog.data.name"
|
||||||
|
type="text"
|
||||||
|
label="Name *"
|
||||||
|
></q-input>
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model.trim="thingDialog.data.email"
|
||||||
|
type="email"
|
||||||
|
label="Name *"
|
||||||
|
></q-input>
|
||||||
|
<q-select
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
emit-value
|
||||||
|
v-model="thingDialog.data.wallet"
|
||||||
|
:options="g.user.walletOptions"
|
||||||
|
label="Wallet *"
|
||||||
|
></q-select>
|
||||||
<div class="row q-mt-lg">
|
<div class="row q-mt-lg">
|
||||||
<q-btn v-if="thingDialog.data.id" unelevated color="primary" type="submit">Update thing</q-btn>
|
<q-btn
|
||||||
<q-btn v-else unelevated color="primary" :disable="thingDialog.data.name == null" type="submit">Create thing</q-btn>
|
v-if="thingDialog.data.id"
|
||||||
|
unelevated
|
||||||
|
color="primary"
|
||||||
|
type="submit"
|
||||||
|
>Update thing</q-btn
|
||||||
|
>
|
||||||
|
<q-btn
|
||||||
|
v-else
|
||||||
|
unelevated
|
||||||
|
color="primary"
|
||||||
|
:disable="thingDialog.data.name == null"
|
||||||
|
type="submit"
|
||||||
|
>Create thing</q-btn
|
||||||
|
>
|
||||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
|
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
|
||||||
</div>
|
</div>
|
||||||
</q-form>
|
</q-form>
|
||||||
@@ -19,7 +51,8 @@
|
|||||||
<q-card flat>
|
<q-card flat>
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<div class="text-h5 q-mb-md">
|
<div class="text-h5 q-mb-md">
|
||||||
{{SITE_TITLE}} Extension Development Guide <small>(Collection of resources for extension developers)</small>
|
{{SITE_TITLE}} Extension Development Guide
|
||||||
|
<small>(Collection of resources for extension developers)</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<q-card unelevated flat>
|
<q-card unelevated flat>
|
||||||
@@ -46,58 +79,129 @@
|
|||||||
<div class="text-h6">Frameworks</div>
|
<div class="text-h6">Frameworks</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<q-splitter
|
<q-splitter v-model="splitterModel">
|
||||||
v-model="splitterModel"
|
|
||||||
>
|
|
||||||
|
|
||||||
<template v-slot:before>
|
<template v-slot:before>
|
||||||
<q-tabs
|
<q-tabs v-model="framworktab" vertical>
|
||||||
v-model="framworktab"
|
<q-tab name="fastapi"
|
||||||
vertical
|
><img src="./static/fastapi-framework.png" />FASTAPI</q-tab
|
||||||
|
>
|
||||||
|
<q-tab name="quasar"
|
||||||
|
><img src="./static/quasar-framework.png" />QUASAR</q-tab
|
||||||
|
>
|
||||||
|
<q-tab name="vuejs"
|
||||||
|
><img src="./static/vuejs-framework.png" />VUE-JS</q-tab
|
||||||
>
|
>
|
||||||
<q-tab name="fastapi"><img src="./static/fastapi-framework.png">FASTAPI</q-tab>
|
|
||||||
<q-tab name="quasar"><img src="./static/quasar-framework.png">QUASAR</q-tab>
|
|
||||||
<q-tab name="vuejs"><img src="./static/vuejs-framework.png">VUE-JS</q-tab>
|
|
||||||
</q-tabs>
|
</q-tabs>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-slot:after>
|
<template v-slot:after>
|
||||||
<q-tab-panels
|
<q-tab-panels v-model="framworktab">
|
||||||
v-model="framworktab"
|
|
||||||
>
|
|
||||||
<q-tab-panel name="fastapi" class="text-body1">
|
<q-tab-panel name="fastapi" class="text-body1">
|
||||||
<img src="./static/fastapilogo.png">
|
<a href="https://fastapi.tiangolo.com/"
|
||||||
<p>LNbits API is built using FastAPI, a high-performance, easy to code API framework.<br/><br/>
|
><img src="./static/fastapilogo.png"
|
||||||
FastAPI auto-generates swagger UI docs for testing endpoints <a class="text-primary" href="../docs">/docs</a></p>
|
/></a>
|
||||||
<code class="bg-grey-3 text-black">views.py</code> is used for setting application routes:
|
<p>
|
||||||
<img src="./static/fastapi-example.png">
|
LNbits API is built using
|
||||||
<code class="bg-grey-3 text-black">views_api.py</code> is used for setting application API endpoints:
|
<a
|
||||||
<img src="./static/fastapi-example2.png">
|
href="https://fastapi.tiangolo.com/"
|
||||||
|
class="text-primary"
|
||||||
|
>FastAPI</a
|
||||||
|
>, a high-performance, easy to code API framework.<br /><br />
|
||||||
|
FastAPI auto-generates swagger UI docs for testing
|
||||||
|
endpoints <a class="text-primary" href="../docs">/docs</a>
|
||||||
|
</p>
|
||||||
|
<code class="bg-grey-3 text-black">views.py</code> is used
|
||||||
|
for setting application routes:
|
||||||
|
<img src="./static/fastapi-example.png" />
|
||||||
|
<code class="bg-grey-3 text-black">views_api.py</code> is
|
||||||
|
used for setting application API endpoints:
|
||||||
|
<img src="./static/fastapi-example2.png" />
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
|
|
||||||
<q-tab-panel name="quasar" class="text-body1">
|
<q-tab-panel name="quasar" class="text-body1">
|
||||||
<img src="./static/quasarlogo.png">
|
<a href="https://quasar.dev/"
|
||||||
<p>LNbits uses Quasar for frontend deisgn elements. Quasar Framework is an open-source Vue.js based framework for building apps.</p>
|
><img src="./static/quasarlogo.png"
|
||||||
|
/></a>
|
||||||
|
<p>
|
||||||
|
LNbits uses
|
||||||
|
<a class="text-primary" href="https://quasar.dev/"
|
||||||
|
>Quasar Framework</a
|
||||||
|
>
|
||||||
|
for frontend deisgn elements. Quasar Framework is an
|
||||||
|
open-source Vue.js based framework for building apps.
|
||||||
|
</p>
|
||||||
|
|
||||||
<p class="text-weight-bold">TIP: Look through <code class="bg-grey-3 text-black">/template</code> files in other extensions for examples.</p>
|
<p class="text-weight-bold">
|
||||||
|
TIP: Look through
|
||||||
|
<code class="bg-grey-3 text-black">/template</code> files
|
||||||
|
in other extensions for examples.
|
||||||
|
</p>
|
||||||
|
|
||||||
<p>In the below example we make a dialogue popup box (box can be triggered <q-btn size="sm" color="primary" @click="thingDialog.show = true">here</q-btn>):
|
<p>
|
||||||
<q-tooltip>Exmple of a tooltip!</q-tooltip></p>
|
In the below example we make a dialogue popup box (box can
|
||||||
<img src="./static/quasar-example.png"><br/><br/>
|
be triggered
|
||||||
|
<q-btn
|
||||||
|
size="sm"
|
||||||
|
color="primary"
|
||||||
|
@click="thingDialog.show = true"
|
||||||
|
>here</q-btn
|
||||||
|
>): <q-tooltip>Exmple of a tooltip!</q-tooltip>
|
||||||
|
</p>
|
||||||
|
<img src="./static/quasar-example.png" /><br /><br />
|
||||||
<div class="text-h6">Useful links:</div>
|
<div class="text-h6">Useful links:</div>
|
||||||
<q-btn color="primary" type="a" href="https://quasar.dev/style/">Style (typography, spacing, etc)</q-btn>
|
<q-btn
|
||||||
<q-btn color="primary" type="a" href="https://quasar.dev/vue-components/">Genral components (cards, buttons, popup dialogs, etc)</q-btn>
|
color="primary"
|
||||||
<q-btn color="primary" type="a" href="https://quasar.dev/layout/grid">Layouts (rows/columns, flexbox)</q-btn>
|
type="a"
|
||||||
|
href="https://quasar.dev/style/"
|
||||||
|
>Style (typography, spacing, etc)</q-btn
|
||||||
|
>
|
||||||
|
<q-btn
|
||||||
|
color="primary"
|
||||||
|
type="a"
|
||||||
|
href="https://quasar.dev/vue-components/"
|
||||||
|
>Genral components (cards, buttons, popup dialogs,
|
||||||
|
etc)</q-btn
|
||||||
|
>
|
||||||
|
<q-btn
|
||||||
|
color="primary"
|
||||||
|
type="a"
|
||||||
|
href="https://quasar.dev/layout/grid"
|
||||||
|
>Layouts (rows/columns, flexbox)</q-btn
|
||||||
|
>
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
|
|
||||||
<q-tab-panel v-if="someBool == true" name="vuejs" class="text-body1">
|
<q-tab-panel
|
||||||
<img src="./static/vuejslogo.png">
|
v-if="someBool == true"
|
||||||
<p>Quasar is built on Vue-JS, so LNbits uses Vue-JS for rendering page elements, organising JS functions and declaring modles and elements.</p>
|
name="vuejs"
|
||||||
<p><code class="bg-grey-3 text-black">v-if</code> can be used to render elements:</p>
|
class="text-body1"
|
||||||
<img src="./static/vif-example.png" style="max-width:800px">
|
>
|
||||||
<p>Typical example of a frontend page JS:</p>
|
<a href="https://vuejs.org/">
|
||||||
<img src="./static/script-example.png" style="max-width:800px">
|
<img src="./static/vuejslogo.png"
|
||||||
|
/></a>
|
||||||
|
<p>
|
||||||
|
LNbits uses
|
||||||
|
<a href="https://vuejs.org/" class="text-primary">Vue</a>
|
||||||
|
components for best-in-class high-performance and
|
||||||
|
responsive performance.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Typical example of Vue components in a frontend script:
|
||||||
|
</p>
|
||||||
|
<img
|
||||||
|
src="./static/script-example.png"
|
||||||
|
style="max-width: 800px"
|
||||||
|
/><br /><br />
|
||||||
|
|
||||||
|
<p>
|
||||||
|
In a page body, models can be called. <br />Content can be
|
||||||
|
conditionally rendered using Vue's
|
||||||
|
<code class="bg-grey-3 text-black">v-if</code>:
|
||||||
|
</p>
|
||||||
|
<img
|
||||||
|
src="./static/vif-example.png"
|
||||||
|
style="max-width: 800px"
|
||||||
|
/>
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
</q-tab-panels>
|
</q-tab-panels>
|
||||||
</template>
|
</template>
|
||||||
@@ -105,7 +209,56 @@
|
|||||||
</div>
|
</div>
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
|
|
||||||
<!--FILE STRUCTURE-->
|
<q-tab-panel name="tools">
|
||||||
|
<div class="text-h6">Useful Tools</div>
|
||||||
|
<div>
|
||||||
|
<q-splitter v-model="splitterModel">
|
||||||
|
<template v-slot:before>
|
||||||
|
<q-tabs v-model="usefultab" vertical>
|
||||||
|
<q-tab name="magicalg">MAGICAL G</q-tab>
|
||||||
|
<q-tab name="exchange">EXCHANGE RATES</q-tab>
|
||||||
|
</q-tabs>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:after>
|
||||||
|
<q-tab-panels v-model="usefultab">
|
||||||
|
<q-tab-panel name="magicalg" class="text-body1">
|
||||||
|
<div class="text-h5 q-mb-md">Magical G</div>
|
||||||
|
<p>
|
||||||
|
A magical "g" (ie
|
||||||
|
<code class="bg-grey-3 text-black"
|
||||||
|
>this.g.user.wallets[0].inkey</code
|
||||||
|
>) is always available, with info about the user, wallets
|
||||||
|
and extensions:
|
||||||
|
</p>
|
||||||
|
<code class="text-caption"
|
||||||
|
>{% raw %}{{ g }}{% endraw %}</code
|
||||||
|
>
|
||||||
|
</q-tab-panel>
|
||||||
|
<q-tab-panel name="exchange">
|
||||||
|
<div class="text-h6">Exchange rates</div>
|
||||||
|
<p>
|
||||||
|
LNbits includes a handy
|
||||||
|
<a
|
||||||
|
href="../docs#/default/api_fiat_as_sats_api_v1_conversion_post"
|
||||||
|
class="text-primary"
|
||||||
|
>exchange rate function</a
|
||||||
|
>, that streams rates from 6 different sources.
|
||||||
|
</p>
|
||||||
|
Exchange rate API:<br />
|
||||||
|
<img src="./static/conversion-example.png" /><br /><br />
|
||||||
|
Exchange rate functions, included using
|
||||||
|
<code class="bg-grey-3 text-black"
|
||||||
|
>from lnbits.utils.exchange_rates import
|
||||||
|
fiat_amount_as_satoshis</code
|
||||||
|
>:<br />
|
||||||
|
<img src="./static/conversion-example2.png" />
|
||||||
|
</q-tab-panel>
|
||||||
|
</q-tab-panels>
|
||||||
|
</template>
|
||||||
|
</q-splitter>
|
||||||
|
</div>
|
||||||
|
</q-tab-panel>
|
||||||
|
|
||||||
<q-tab-panel name="structure">
|
<q-tab-panel name="structure">
|
||||||
<div class="text-h6">File Structure</div>
|
<div class="text-h6">File Structure</div>
|
||||||
@@ -121,46 +274,6 @@
|
|||||||
<div class="text-h6">Submission</div>
|
<div class="text-h6">Submission</div>
|
||||||
Coming soon...
|
Coming soon...
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
|
|
||||||
<q-tab-panel name="tools">
|
|
||||||
<div class="text-h6">Useful Tools</div>
|
|
||||||
<div>
|
|
||||||
<q-splitter
|
|
||||||
v-model="splitterModel"
|
|
||||||
>
|
|
||||||
|
|
||||||
<template v-slot:before>
|
|
||||||
<q-tabs
|
|
||||||
v-model="usefultab"
|
|
||||||
vertical
|
|
||||||
>
|
|
||||||
<q-tab name="magicalg">MAGICAL G</q-tab>
|
|
||||||
<q-tab name="conversion">CONVERSION</q-tab>
|
|
||||||
</q-tabs>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:after>
|
|
||||||
<q-tab-panels
|
|
||||||
v-model="usefultab"
|
|
||||||
>
|
|
||||||
<q-tab-panel name="magicalg" class="text-body1">
|
|
||||||
<div class="text-h5 q-mb-md">Magical G</div>
|
|
||||||
<p>
|
|
||||||
A magical "g" (ie <code class="bg-grey-3 text-black">this.g.user.wallets[0].inkey</code>) is always available, with info about the user, wallets and
|
|
||||||
extensions:
|
|
||||||
</p>
|
|
||||||
<code class="text-caption">{% raw %}{{ g }}{% endraw %}</code>
|
|
||||||
</q-tab-panel>
|
|
||||||
<q-tab-panel name="conversion">
|
|
||||||
<div class="text-h6">Conversion </div>
|
|
||||||
Coming soon...
|
|
||||||
</q-tab-panel>
|
|
||||||
</q-tab-panels>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
</q-splitter>
|
|
||||||
</div>
|
|
||||||
</q-tab-panel>
|
|
||||||
</q-tab-panels>
|
</q-tab-panels>
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
@@ -190,9 +303,9 @@ var someMapObject = obj => {
|
|||||||
someBool: true,
|
someBool: true,
|
||||||
splitterModel: 20,
|
splitterModel: 20,
|
||||||
exampleData: [],
|
exampleData: [],
|
||||||
tab: "frameworks",
|
tab: 'frameworks',
|
||||||
framworktab: "fastapi",
|
framworktab: 'fastapi',
|
||||||
usefultab: "magicalg"
|
usefultab: 'magicalg'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
///// Where functions live /////
|
///// Where functions live /////
|
||||||
@@ -219,7 +332,7 @@ var someMapObject = obj => {
|
|||||||
///// To run on startup /////
|
///// To run on startup /////
|
||||||
created: function () {
|
created: function () {
|
||||||
self = this // Often used to run a real object, rather than the event (all a bit confusing really)
|
self = this // Often used to run a real object, rather than the event (all a bit confusing really)
|
||||||
self.exampleFunction("lorum")
|
self.exampleFunction('lorum')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@@ -6,6 +6,7 @@ from . import example_ext
|
|||||||
|
|
||||||
# add your endpoints here
|
# add your endpoints here
|
||||||
|
|
||||||
|
|
||||||
@example_ext.get("/api/v1/test/{test_data}")
|
@example_ext.get("/api/v1/test/{test_data}")
|
||||||
async def api_example(test_data):
|
async def api_example(test_data):
|
||||||
# Do some python things and return the data
|
# Do some python things and return the data
|
||||||
|
Reference in New Issue
Block a user