Merge remote-tracking branch 'origin/main' into improved-pwa

This commit is contained in:
Lee Salminen 2022-07-05 09:21:41 -06:00
commit 80f8d83548
26 changed files with 404 additions and 115 deletions

1
.gitignore vendored
View File

@ -33,3 +33,4 @@ __bundle__
node_modules
lnbits/static/bundle.*
docker

View File

@ -2,7 +2,7 @@
all: format check requirements.txt
format: prettier black
format: black
check: mypy checkprettier checkblack

View File

@ -31,7 +31,7 @@
<code>[&lt;copilot_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/copilot -d '{"title":
>curl -X POST {{ request.base_url }}copilot/api/v1/copilot -d '{"title":
&lt;string&gt;, "animation": &lt;string&gt;,
"show_message":&lt;string&gt;, "amount": &lt;integer&gt;,
"lnurl_title": &lt;string&gt;}' -H "Content-type: application/json"
@ -59,7 +59,7 @@
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X POST {{ request.base_url
}}api/v1/copilot/&lt;copilot_id&gt; -d '{"title": &lt;string&gt;,
}}copilot/api/v1/copilot/&lt;copilot_id&gt; -d '{"title": &lt;string&gt;,
"animation": &lt;string&gt;, "show_message":&lt;string&gt;,
"amount": &lt;integer&gt;, "lnurl_title": &lt;string&gt;}' -H
"Content-type: application/json" -H "X-Api-Key:
@ -87,7 +87,7 @@
<code>[&lt;copilot_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/copilot/&lt;copilot_id&gt;
>curl -X GET {{ request.base_url }}copilot/api/v1/copilot/&lt;copilot_id&gt;
-H "X-Api-Key: {{ user.wallets[0].inkey }}"
</code>
</q-card-section>
@ -110,7 +110,7 @@
<code>[&lt;copilot_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/copilots -H "X-Api-Key: {{
>curl -X GET {{ request.base_url }}copilot/api/v1/copilots -H "X-Api-Key: {{
user.wallets[0].inkey }}"
</code>
</q-card-section>
@ -136,7 +136,7 @@
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X DELETE {{ request.base_url
}}api/v1/copilot/&lt;copilot_id&gt; -H "X-Api-Key: {{
}}copilot/api/v1/copilot/&lt;copilot_id&gt; -H "X-Api-Key: {{
user.wallets[0].adminkey }}"
</code>
</q-card-section>
@ -161,7 +161,7 @@
<code></code>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X GET {{ request.base_url }}/api/v1/copilot/ws/&lt;string,
>curl -X GET {{ request.base_url }}copilot/api/v1/copilot/ws/&lt;string,
copilot_id&gt;/&lt;string, comment&gt;/&lt;string, gif name&gt; -H
"X-Api-Key: {{ user.wallets[0].adminkey }}"
</code>

View File

@ -37,7 +37,7 @@
<code>[&lt;jukebox_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/jukebox -H "X-Api-Key: {{
>curl -X GET {{ request.base_url }}jukebox/api/v1/jukebox -H "X-Api-Key: {{
user.wallets[0].adminkey }}"
</code>
</q-card-section>
@ -59,7 +59,7 @@
<code>&lt;jukebox_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/jukebox/&lt;juke_id&gt; -H
>curl -X GET {{ request.base_url }}jukebox/api/v1/jukebox/&lt;juke_id&gt; -H
"X-Api-Key: {{ user.wallets[0].adminkey }}"
</code>
</q-card-section>
@ -86,7 +86,7 @@
<code>&lt;jukbox_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/jukebox/ -d '{"user":
>curl -X POST {{ request.base_url }}jukebox/api/v1/jukebox/ -d '{"user":
&lt;string, user_id&gt;, "title": &lt;string&gt;,
"wallet":&lt;string&gt;, "sp_user": &lt;string,
spotify_user_account&gt;, "sp_secret": &lt;string,
@ -116,7 +116,7 @@
<code>&lt;jukebox_object&gt;</code>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X DELETE {{ request.base_url }}api/v1/jukebox/&lt;juke_id&gt;
>curl -X DELETE {{ request.base_url }}jukebox/api/v1/jukebox/&lt;juke_id&gt;
-H "X-Api-Key: {{ user.wallets[0].adminkey }}"
</code>
</q-card-section>

View File

@ -38,7 +38,7 @@
<code>[&lt;livestream_object&gt;, ...]</code>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X GET {{ request.url_root }}api/v1/livestream -H "X-Api-Key: {{
>curl -X GET {{ request.base_url }}livestream/api/v1/livestream -H "X-Api-Key: {{
user.wallets[0].inkey }}"
</code>
</q-card-section>
@ -59,8 +59,8 @@
</h5>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X PUT {{ request.url_root
}}api/v1/livestream/track/&lt;track_id&gt; -H "X-Api-Key: {{
>curl -X PUT {{ request.base_url }}
livestream/api/v1/livestream/track/&lt;track_id&gt; -H "X-Api-Key: {{
user.wallets[0].inkey }}"
</code>
</q-card-section>
@ -81,8 +81,8 @@
</h5>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X PUT {{ request.url_root
}}api/v1/livestream/fee/&lt;fee_pct&gt; -H "X-Api-Key: {{
>curl -X PUT {{ request.base_url }}
livestream/api/v1/livestream/fee/&lt;fee_pct&gt; -H "X-Api-Key: {{
user.wallets[0].inkey }}"
</code>
</q-card-section>
@ -109,7 +109,8 @@
</h5>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X POST {{ request.url_root }}api/v1/livestream/tracks -d
>curl -X POST {{ request.base_url }}
livestream/api/v1/livestream/tracks -d
'{"name": &lt;string&gt;, "download_url": &lt;string&gt;,
"price_msat": &lt;integer&gt;, "producer_id": &lt;integer&gt;,
"producer_name": &lt;string&gt;}' -H "Content-type: application/json"
@ -123,6 +124,7 @@
dense
expand-separator
label="Delete a withdraw link"
class="q-pb-md"
>
<q-card>
<q-card-section>
@ -136,8 +138,8 @@
<code></code>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X DELETE {{ request.url_root
}}api/v1/livestream/tracks/&lt;track_id&gt; -H "X-Api-Key: {{
>curl -X DELETE {{ request.base_url }}
livestream/api/v1/livestream/tracks/&lt;track_id&gt; -H "X-Api-Key: {{
user.wallets[0].inkey }}"
</code>
</q-card-section>

View File

@ -45,7 +45,7 @@
<code>JSON list of users</code>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X GET {{ request.url_root }}lnaddress/api/v1/domains -H
>curl -X GET {{ request.base_url }}lnaddress/api/v1/domains -H
"X-Api-Key: {{ user.wallets[0].inkey }}"
</code>
</q-card-section>
@ -81,7 +81,7 @@
>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X POST {{ request.url_root }}lnaddress/api/v1/domains -d
>curl -X POST {{ request.base_url }}lnaddress/api/v1/domains -d
'{"wallet": "{{ user.wallets[0].id }}", "domain": &lt;string&gt;,
"cf_token": &lt;string&gt;,"cf_zone_id": &lt;string&gt;,"webhook":
&lt;Optional string&gt; ,"cost": &lt;integer&gt;}' -H "X-Api-Key: {{
@ -101,7 +101,7 @@
<code>{"X-Api-Key": &lt;string&gt;}</code>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X DELETE {{ request.url_root
>curl -X DELETE {{ request.base_url
}}lnaddress/api/v1/domains/&lt;domain_id&gt; -H "X-Api-Key: {{
user.wallets[0].inkey }}"
</code>
@ -122,7 +122,7 @@
<code>JSON list of addresses</code>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X GET {{ request.url_root }}lnaddress/api/v1/addresses -H
>curl -X GET {{ request.base_url }}lnaddress/api/v1/addresses -H
"X-Api-Key: {{ user.wallets[0].inkey }}"
</code>
</q-card-section>
@ -142,14 +142,14 @@
<code>JSON list of addresses</code>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X GET {{ request.url_root
>curl -X GET {{ request.base_url
}}lnaddress/api/v1/address/&lt;domain&gt;/&lt;username&gt;/&lt;wallet_key&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="POST address">
<q-expansion-item group="api" dense expand-separator label="POST address" class="q-pb-md">
<q-card>
<q-card-section>
<code
@ -160,7 +160,7 @@
<code>{"X-Api-Key": &lt;string&gt;}</code>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X POST {{ request.url_root
>curl -X POST {{ request.base_url
}}lnaddress/api/v1/address/&lt;domain_id&gt; -d '{"domain":
&lt;string&gt;, "username": &lt;string&gt;,"email": &lt;Optional
string&gt;, "wallet_endpoint": &lt;string&gt;, "wallet_key":

View File

@ -39,7 +39,7 @@
<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":
>curl -X POST {{ request.base_url }}lnurldevice/api/v1/lnurlpos -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 }}"
@ -71,7 +71,7 @@
<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":
}}lnurldevice/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 }}"
@ -104,7 +104,7 @@
<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: {{
}}lnurldevice/api/v1/lnurlpos/&lt;lnurldevice_id&gt; -H "X-Api-Key: {{
user.wallets[0].inkey }}"
</code>
</q-card-section>
@ -133,7 +133,7 @@
<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
>curl -X GET {{ request.base_url }}lnurldevice/api/v1/lnurlpos -H
"X-Api-Key: {{ user.wallets[0].inkey }}"
</code>
</q-card-section>
@ -159,7 +159,7 @@
<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: {{
}}lnurldevice/api/v1/lnurlpos/&lt;lnurldevice_id&gt; -H "X-Api-Key: {{
user.wallets[0].adminkey }}"
</code>
</q-card-section>

View File

@ -17,7 +17,7 @@
<code>[&lt;pay_link_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/links -H "X-Api-Key: {{
>curl -X GET {{ request.base_url }}lnurlp/api/v1/links -H "X-Api-Key: {{
user.wallets[0].inkey }}"
</code>
</q-card-section>
@ -39,7 +39,7 @@
<code>{"lnurl": &lt;string&gt;}</code>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X GET {{ request.base_url }}api/v1/links/&lt;pay_id&gt; -H
>curl -X GET {{ request.base_url }}lnurlp/api/v1/links/&lt;pay_id&gt; -H
"X-Api-Key: {{ user.wallets[0].inkey }}"
</code>
</q-card-section>
@ -68,7 +68,7 @@
<code>{"lnurl": &lt;string&gt;}</code>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X POST {{ request.base_url }}api/v1/links -d '{"description":
>curl -X POST {{ request.base_url }}lnurlp/api/v1/links -d '{"description":
&lt;string&gt;, "amount": &lt;integer&gt;, "max": &lt;integer&gt;,
"min": &lt;integer&gt;, "comment_chars": &lt;integer&gt;}' -H
"Content-type: application/json" -H "X-Api-Key: {{
@ -99,7 +99,7 @@
<code>{"lnurl": &lt;string&gt;}</code>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X PUT {{ request.base_url }}api/v1/links/&lt;pay_id&gt; -d
>curl -X PUT {{ request.base_url }}lnurlp/api/v1/links/&lt;pay_id&gt; -d
'{"description": &lt;string&gt;, "amount": &lt;integer&gt;}' -H
"Content-type: application/json" -H "X-Api-Key: {{
user.wallets[0].adminkey }}"
@ -126,7 +126,7 @@
<code></code>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X DELETE {{ request.base_url }}api/v1/links/&lt;pay_id&gt; -H
>curl -X DELETE {{ request.base_url }}lnurlp/api/v1/links/&lt;pay_id&gt; -H
"X-Api-Key: {{ user.wallets[0].adminkey }}"
</code>
</q-card-section>

View File

@ -63,7 +63,7 @@
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X GET {{ request.base_url
}}/offlineshop/api/v1/offlineshop/items -H "Content-Type:
}}offlineshop/api/v1/offlineshop/items -H "Content-Type:
application/json" -H "X-Api-Key: {{ user.wallets[0].inkey }}" -d
'{"name": &lt;string&gt;, "description": &lt;string&gt;, "image":
&lt;data-uri string&gt;, "price": &lt;integer&gt;, "unit": &lt;"sat"
@ -96,7 +96,7 @@
>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X GET {{ request.base_url }}/offlineshop/api/v1/offlineshop -H
>curl -X GET {{ request.base_url }}offlineshop/api/v1/offlineshop -H
"X-Api-Key: {{ user.wallets[0].inkey }}"
</code>
</q-card-section>
@ -118,7 +118,7 @@
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X GET {{ request.base_url
}}/offlineshop/api/v1/offlineshop/items/&lt;item_id&gt; -H
}}offlineshop/api/v1/offlineshop/items/&lt;item_id&gt; -H
"Content-Type: application/json" -H "X-Api-Key: {{
user.wallets[0].inkey }}" -d '{"name": &lt;string&gt;, "description":
&lt;string&gt;, "image": &lt;data-uri string&gt;, "price":
@ -127,7 +127,7 @@
</q-card-section>
</q-card>
</q-expansion-item>
<q-expansion-item group="api" dense expand-separator label="Delete item">
<q-expansion-item group="api" dense expand-separator label="Delete item" class="q-pb-md">
<q-card>
<q-card-section>
<code><span class="text-blue">DELETE</span></code>
@ -138,7 +138,7 @@
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X GET {{ request.base_url
}}/offlineshop/api/v1/offlineshop/items/&lt;item_id&gt; -H "X-Api-Key:
}}offlineshop/api/v1/offlineshop/items/&lt;item_id&gt; -H "X-Api-Key:
{{ user.wallets[0].inkey }}"
</code>
</q-card-section>

View File

@ -17,7 +17,7 @@
<code>[&lt;paywall_object&gt;, ...]</code>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X GET {{ request.url_root }}api/v1/paywalls -H "X-Api-Key: {{
>curl -X GET {{ request.base_url }}paywall/api/v1/paywalls -H "X-Api-Key: {{
user.wallets[0].inkey }}"
</code>
</q-card-section>
@ -48,7 +48,7 @@
>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X POST {{ request.url_root }}api/v1/paywalls -d '{"url":
>curl -X POST {{ request.base_url }}paywall/api/v1/paywalls -d '{"url":
&lt;string&gt;, "memo": &lt;string&gt;, "description": &lt;string&gt;,
"amount": &lt;integer&gt;, "remembers": &lt;boolean&gt;}' -H
"Content-type: application/json" -H "X-Api-Key: {{
@ -80,8 +80,8 @@
>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X POST {{ request.url_root
}}api/v1/paywalls/&lt;paywall_id&gt;/invoice -d '{"amount":
>curl -X POST {{ request.base_url
}}paywall/api/v1/paywalls/&lt;paywall_id&gt;/invoice -d '{"amount":
&lt;integer&gt;}' -H "Content-type: application/json"
</code>
</q-card-section>
@ -111,8 +111,8 @@
>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X POST {{ request.url_root
}}api/v1/paywalls/&lt;paywall_id&gt;/check_invoice -d
>curl -X POST {{ request.base_url
}}paywall/api/v1/paywalls/&lt;paywall_id&gt;/check_invoice -d
'{"payment_hash": &lt;string&gt;}' -H "Content-type: application/json"
</code>
</q-card-section>
@ -137,8 +137,8 @@
<code></code>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X DELETE {{ request.url_root
}}api/v1/paywalls/&lt;paywall_id&gt; -H "X-Api-Key: {{
>curl -X DELETE {{ request.base_url
}}paywall/api/v1/paywalls/&lt;paywall_id&gt; -H "X-Api-Key: {{
user.wallets[0].adminkey }}"
</code>
</q-card-section>

View File

@ -17,7 +17,7 @@
<code>[&lt;satsdice_link_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/links -H "X-Api-Key: {{
>curl -X GET {{ request.base_url }}satsdice/api/v1/links -H "X-Api-Key: {{
user.wallets[0].inkey }}"
</code>
</q-card-section>
@ -44,7 +44,7 @@
<code>{"lnurl": &lt;string&gt;}</code>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X GET {{ request.base_url }}api/v1/links/&lt;satsdice_id&gt; -H
>curl -X GET {{ request.base_url }}satsdice/api/v1/links/&lt;satsdice_id&gt; -H
"X-Api-Key: {{ user.wallets[0].inkey }}"
</code>
</q-card-section>
@ -73,7 +73,7 @@
<code>{"lnurl": &lt;string&gt;}</code>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X POST {{ request.base_url }}api/v1/links -d '{"title":
>curl -X POST {{ request.base_url }}satsdice/api/v1/links -d '{"title":
&lt;string&gt;, "min_satsdiceable": &lt;integer&gt;,
"max_satsdiceable": &lt;integer&gt;, "uses": &lt;integer&gt;,
"wait_time": &lt;integer&gt;, "is_unique": &lt;boolean&gt;}' -H
@ -109,7 +109,7 @@
<code>{"lnurl": &lt;string&gt;}</code>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X PUT {{ request.base_url }}api/v1/links/&lt;satsdice_id&gt; -d
>curl -X PUT {{ request.base_url }}satsdice/api/v1/links/&lt;satsdice_id&gt; -d
'{"title": &lt;string&gt;, "min_satsdiceable": &lt;integer&gt;,
"max_satsdiceable": &lt;integer&gt;, "uses": &lt;integer&gt;,
"wait_time": &lt;integer&gt;, "is_unique": &lt;boolean&gt;}' -H
@ -137,7 +137,7 @@
<code></code>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X DELETE {{ request.base_url }}api/v1/links/&lt;satsdice_id&gt;
>curl -X DELETE {{ request.base_url }}satsdice/api/v1/links/&lt;satsdice_id&gt;
-H "X-Api-Key: {{ user.wallets[0].adminkey }}"
</code>
</q-card-section>
@ -165,7 +165,7 @@
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X GET {{ request.base_url
}}api/v1/links/&lt;the_hash&gt;/&lt;lnurl_id&gt; -H "X-Api-Key: {{
}}satsdice/api/v1/links/&lt;the_hash&gt;/&lt;lnurl_id&gt; -H "X-Api-Key: {{
user.wallets[0].inkey }}"
</code>
</q-card-section>
@ -186,7 +186,7 @@
>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X GET {{ request.base_url }}/satsdice/img/&lt;lnurl_id&gt;"
>curl -X GET {{ request.base_url }}satsdice/img/&lt;lnurl_id&gt;"
</code>
</q-card-section>
</q-card>

View File

@ -32,7 +32,7 @@
<code>[&lt;charge_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/charge -d
>curl -X POST {{ request.base_url }}satspay/api/v1/charge -d
'{"onchainwallet": &lt;string, watchonly_wallet_id&gt;,
"description": &lt;string&gt;, "webhook":&lt;string&gt;, "time":
&lt;integer&gt;, "amount": &lt;integer&gt;, "lnbitswallet":
@ -60,7 +60,7 @@
<code>[&lt;charge_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/charge/&lt;charge_id&gt;
>curl -X POST {{ request.base_url }}satspay/api/v1/charge/&lt;charge_id&gt;
-d '{"onchainwallet": &lt;string, watchonly_wallet_id&gt;,
"description": &lt;string&gt;, "webhook":&lt;string&gt;, "time":
&lt;integer&gt;, "amount": &lt;integer&gt;, "lnbitswallet":
@ -89,7 +89,7 @@
<code>[&lt;charge_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/charge/&lt;charge_id&gt;
>curl -X GET {{ request.base_url }}satspay/api/v1/charge/&lt;charge_id&gt;
-H "X-Api-Key: {{ user.wallets[0].inkey }}"
</code>
</q-card-section>
@ -112,7 +112,7 @@
<code>[&lt;charge_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/charges -H "X-Api-Key: {{
>curl -X GET {{ request.base_url }}satspay/api/v1/charges -H "X-Api-Key: {{
user.wallets[0].inkey }}"
</code>
</q-card-section>
@ -123,7 +123,6 @@
dense
expand-separator
label="Delete a pay link"
class="q-pb-md"
>
<q-card>
<q-card-section>
@ -138,13 +137,18 @@
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X DELETE {{ request.base_url
}}api/v1/charge/&lt;charge_id&gt; -H "X-Api-Key: {{
}}satspay/api/v1/charge/&lt;charge_id&gt; -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 balances">
<q-expansion-item
group="api"
dense expand-separator
label="Get balances"
class="q-pb-md"
>
<q-card>
<q-card-section>
<code
@ -161,7 +165,7 @@
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X GET {{ request.base_url
}}api/v1/charges/balance/&lt;charge_id&gt; -H "X-Api-Key: {{
}}satspay/api/v1/charges/balance/&lt;charge_id&gt; -H "X-Api-Key: {{
user.wallets[0].inkey }}"
</code>
</q-card-section>

View File

@ -52,7 +52,7 @@
>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X GET {{ request.base_url }}api/v1/livestream -H "X-Api-Key: {{
>curl -X GET {{ request.base_url }}splitpayments/api/v1/targets -H "X-Api-Key: {{
user.wallets[0].inkey }}"
</code>
</q-card-section>
@ -63,6 +63,7 @@
dense
expand-separator
label="Set Target Wallets"
class="q-pb-md"
>
<q-card>
<q-card-section>
@ -78,7 +79,7 @@
</h5>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X PUT {{ request.base_url }}api/v1/splitpayments/targets -H
>curl -X PUT {{ request.base_url }}splitpayments/api/v1/targets -H
"X-Api-Key: {{ user.wallets[0].adminkey }}" -H 'Content-Type:
application/json' -d '{"targets": [{"wallet": &lt;wallet id or invoice
key&gt;, "alias": &lt;name to identify this&gt;, "percent": &lt;number

View File

@ -1,7 +1,10 @@
import asyncio
from fastapi import APIRouter
from lnbits.db import Database
from lnbits.helpers import template_renderer
from lnbits.tasks import catch_everything_and_restart
db = Database("ext_tpos")
@ -12,5 +15,11 @@ def tpos_renderer():
return template_renderer(["lnbits/extensions/tpos/templates"])
from .tasks import wait_for_paid_invoices
from .views_api import * # noqa
from .views import * # noqa
def tpos_start():
loop = asyncio.get_event_loop()
loop.create_task(catch_everything_and_restart(wait_for_paid_invoices))

View File

@ -2,5 +2,5 @@
"name": "TPoS",
"short_description": "A shareable PoS terminal!",
"icon": "dialpad",
"contributors": ["talvasconcelos", "arcbtc"]
"contributors": ["talvasconcelos", "arcbtc", "leesalminen"]
}

View File

@ -10,10 +10,17 @@ async def create_tpos(wallet_id: str, data: CreateTposData) -> TPoS:
tpos_id = urlsafe_short_hash()
await db.execute(
"""
INSERT INTO tpos.tposs (id, wallet, name, currency)
VALUES (?, ?, ?, ?)
INSERT INTO tpos.tposs (id, wallet, name, currency, tip_options, tip_wallet)
VALUES (?, ?, ?, ?, ?, ?)
""",
(tpos_id, wallet_id, data.name, data.currency),
(
tpos_id,
wallet_id,
data.name,
data.currency,
data.tip_options,
data.tip_wallet,
),
)
tpos = await get_tpos(tpos_id)

View File

@ -12,3 +12,25 @@ async def m001_initial(db):
);
"""
)
async def m002_addtip_wallet(db):
"""
Add tips to tposs table
"""
await db.execute(
"""
ALTER TABLE tpos.tposs ADD tip_wallet TEXT NULL;
"""
)
async def m003_addtip_options(db):
"""
Add tips to tposs table
"""
await db.execute(
"""
ALTER TABLE tpos.tposs ADD tip_options TEXT NULL;
"""
)

View File

@ -6,6 +6,8 @@ from pydantic import BaseModel
class CreateTposData(BaseModel):
name: str
currency: str
tip_options: str
tip_wallet: str
class TPoS(BaseModel):
@ -13,6 +15,8 @@ class TPoS(BaseModel):
wallet: str
name: str
currency: str
tip_options: str
tip_wallet: str
@classmethod
def from_row(cls, row: Row) -> "TPoS":

View File

@ -0,0 +1,70 @@
import asyncio
import json
from lnbits.core import db as core_db
from lnbits.core.crud import create_payment
from lnbits.core.models import Payment
from lnbits.helpers import urlsafe_short_hash
from lnbits.tasks import internal_invoice_queue, register_invoice_listener
from .crud import get_tpos
async def wait_for_paid_invoices():
invoice_queue = asyncio.Queue()
register_invoice_listener(invoice_queue)
while True:
payment = await invoice_queue.get()
await on_invoice_paid(payment)
async def on_invoice_paid(payment: Payment) -> None:
if "tpos" == payment.extra.get("tag") and payment.extra.get("tipSplitted"):
# already splitted, ignore
return
# now we make some special internal transfers (from no one to the receiver)
tpos = await get_tpos(payment.extra.get("tposId"))
tipAmount = payment.extra.get("tipAmount")
if tipAmount is None:
# no tip amount
return
tipAmount = tipAmount * 1000
# mark the original payment with one extra key, "splitted"
# (this prevents us from doing this process again and it's informative)
# and reduce it by the amount we're going to send to the producer
await core_db.execute(
"""
UPDATE apipayments
SET extra = ?, amount = amount - ?
WHERE hash = ?
AND checking_id NOT LIKE 'internal_%'
""",
(
json.dumps(dict(**payment.extra, tipSplitted=True)),
tipAmount,
payment.payment_hash,
),
)
# perform the internal transfer using the same payment_hash
internal_checking_id = f"internal_{urlsafe_short_hash()}"
await create_payment(
wallet_id=tpos.tip_wallet,
checking_id=internal_checking_id,
payment_request="",
payment_hash=payment.payment_hash,
amount=tipAmount,
memo=payment.memo,
pending=False,
extra={"tipSplitted": True},
)
# manually send this for now
await internal_invoice_queue.put(internal_checking_id)
return

View File

@ -17,7 +17,7 @@
<code>[&lt;tpos_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/tposs -H "X-Api-Key:
>curl -X GET {{ request.base_url }}tpos/api/v1/tposs -H "X-Api-Key:
&lt;invoice_key&gt;"
</code>
</q-card-section>
@ -42,7 +42,7 @@
>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X POST {{ request.base_url }}api/v1/tposs -d '{"name":
>curl -X POST {{ request.base_url }}tpos/api/v1/tposs -d '{"name":
&lt;string&gt;, "currency": &lt;string&gt;}' -H "Content-type:
application/json" -H "X-Api-Key: &lt;admin_key&gt;"
</code>
@ -69,7 +69,7 @@
<code></code>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X DELETE {{ request.base_url }}api/v1/tposs/&lt;tpos_id&gt; -H
>curl -X DELETE {{ request.base_url }}tpos/api/v1/tposs/&lt;tpos_id&gt; -H
"X-Api-Key: &lt;admin_key&gt;"
</code>
</q-card-section>

View File

@ -54,7 +54,7 @@
></q-btn>
</q-td>
<q-td v-for="col in props.cols" :key="col.name" :props="props">
{{ col.value }}
{{ (col.name == 'tip_options' ? JSON.parse(col.value).join(", ") : col.value) }}
</q-td>
<q-td auto-width>
<q-btn
@ -116,6 +116,29 @@
:options="currencyOptions"
label="Currency *"
></q-select>
<q-select
filled
dense
emit-value
v-model="formDialog.data.tip_wallet"
:options="g.user.walletOptions"
label="Tip Wallet"
></q-select>
<q-select
filled
multiple
dense
emit-value
v-model="formDialog.data.tip_options"
v-if="formDialog.data.tip_wallet"
use-input
use-chips
multiple
hide-dropdown-icon
input-debounce="0"
new-value-mode="add-unique"
label="Tip % Options"
></q-select>
<div class="row q-mt-lg">
<q-btn
unelevated
@ -333,7 +356,19 @@
align: 'left',
label: 'Currency',
field: 'currency'
}
},
{
name: 'tip_wallet',
align: 'left',
label: "Tip Wallet",
field: "tip_wallet",
},
{
name: 'tip_options',
align: 'left',
label: "Tip Options %",
field: "tip_options",
},
],
pagination: {
rowsPerPage: 10
@ -367,7 +402,9 @@
createTPoS: function () {
var data = {
name: this.formDialog.data.name,
currency: this.formDialog.data.currency
currency: this.formDialog.data.currency,
tip_options: (this.formDialog.data.tip_options ? JSON.stringify(this.formDialog.data.tip_options.map(str => parseInt(str))) : JSON.stringify([])),
tip_wallet: this.formDialog.data.tip_wallet || "",
}
var self = this

View File

@ -1,5 +1,17 @@
{% extends "public.html" %} {% block toolbar_title %}{{ tpos.name }}{% endblock
%} {% block footer %}{% endblock %} {% block page_container %}
<link rel="manifest" href="/tpos/manifest/{{ tpos.id }}.webmanifest" />
{% extends "public.html" %}
{% block toolbar_title %}
{{ tpos.name }}
<q-btn
flat
dense
size="md"
@click.prevent="urlDialog.show = true"
icon="share"
color="white"
></q-btn>
{% endblock %}
{% block footer %}{% endblock %} {% block page_container %}
<q-page-container>
<q-page>
<q-page-sticky v-if="exchangeRate" expand position="top">
@ -43,16 +55,6 @@
color="primary"
>3</q-btn
>
<q-btn
unelevated
@click="stack = []"
size="xl"
:outline="!($q.dark.isActive)"
rounded
color="primary"
class="btn-cancel"
>C</q-btn
>
<q-btn
unelevated
@click="stack.push(4)"
@ -107,17 +109,6 @@
color="primary"
>9</q-btn
>
<q-btn
unelevated
:disabled="amount == 0"
@click="showInvoice()"
size="xl"
:outline="!($q.dark.isActive)"
rounded
color="primary"
class="btn-confirm"
>OK</q-btn
>
<q-btn
unelevated
@click="stack.splice(-1, 1)"
@ -138,12 +129,24 @@
>
<q-btn
unelevated
@click="urlDialog.show = true"
@click="stack = []"
size="xl"
:outline="!($q.dark.isActive)"
rounded
color="primary"
>#</q-btn
class="btn-cancel"
>C</q-btn
>
<q-btn
unelevated
:disabled="amount == 0"
@click="submitForm()"
size="xl"
:outline="!($q.dark.isActive)"
rounded
color="primary"
class="btn-confirm"
>OK</q-btn
>
</div>
</div>
@ -176,6 +179,38 @@
</div>
</q-card>
</q-dialog>
<q-dialog
v-model="tipDialog.show"
position="top"
>
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
<div class="text-center q-mb-xl">
<b style="font-size: 24px;">Would you like to leave a tip?</b>
</div>
<div class="text-center q-mb-xl">
<q-btn
style="padding: 10px; margin: 3px;"
unelevated
@click="processTipSelection(tip)"
size="xl"
:outline="!($q.dark.isActive)"
rounded
color="primary"
v-for="tip in this.tip_options"
:key="tip"
>{% raw %}{{ tip }}{% endraw %}%</q-btn
>
</div>
<div class="text-center q-mb-xl">
<p><a @click="processTipSelection(0)"> No, thanks</a></p>
</div>
<div class="row q-mt-lg">
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Close</q-btn>
</div>
</q-card>
</q-dialog>
<q-dialog v-model="urlDialog.show" position="top">
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
<q-responsive :ratio="1" class="q-mx-xl q-mb-md">
@ -214,6 +249,10 @@
</q-page-container>
{% endblock %} {% block styles %}
<style>
* {
touch-action: manipulation;
}
.keypad {
display: grid;
grid-gap: 8px;
@ -225,9 +264,8 @@
height: 100%;
}
.btn-cancel,
.btn-confirm {
grid-row: auto/span 2;
.keypad .btn-confirm {
grid-area: 1 / 4 / 5 / 4;
}
</style>
{% endblock %} {% block scripts %}
@ -241,14 +279,19 @@
return {
tposId: '{{ tpos.id }}',
currency: '{{ tpos.currency }}',
tip_options: JSON.parse('{{ tpos.tip_options }}'),
exchangeRate: null,
stack: [],
tipAmount: 0.00,
invoiceDialog: {
show: false,
data: null,
dismissMsg: null,
paymentChecker: null
},
tipDialog: {
show: false,
},
urlDialog: {
show: false
},
@ -269,6 +312,10 @@
if (!this.exchangeRate) return 0
return Math.ceil((this.amount / this.exchangeRate) * 100000000)
},
tipAmountSat: function () {
if (!this.exchangeRate) return 0
return Math.ceil((this.tipAmount / this.exchangeRate) * 100000000)
},
fsat: function () {
console.log('sat', this.sat, LNbits.utils.formatSat(this.sat))
return LNbits.utils.formatSat(this.sat)
@ -277,12 +324,46 @@
methods: {
closeInvoiceDialog: function () {
this.stack = []
this.tipAmount = 0.00
var dialog = this.invoiceDialog
setTimeout(function () {
clearInterval(dialog.paymentChecker)
dialog.dismissMsg()
}, 3000)
},
processTipSelection: function (selectedTipOption) {
this.tipDialog.show = false
if(selectedTipOption) {
const tipAmount = parseFloat(parseFloat((selectedTipOption / 100) * this.amount))
const subtotal = parseFloat(this.amount)
const grandTotal = parseFloat((tipAmount + subtotal).toFixed(2))
const totalString = grandTotal.toFixed(2).toString()
this.stack = []
for (var i = 0; i < totalString.length; i++) {
const char = totalString[i]
if(char !== ".") {
this.stack.push(char)
}
}
this.tipAmount = tipAmount
}
this.showInvoice()
},
submitForm: function() {
if(this.tip_options.length) {
this.showTipModal()
} else {
this.showInvoice()
}
},
showTipModal: function() {
this.tipDialog.show = true
},
showInvoice: function () {
var self = this
var dialog = this.invoiceDialog
@ -290,7 +371,8 @@
axios
.post('/tpos/api/v1/tposs/' + this.tposId + '/invoices', null, {
params: {
amount: this.sat
amount: this.sat,
tipAmount: this.tipAmountSat,
}
})
.then(function (response) {

View File

@ -8,6 +8,10 @@ from starlette.responses import HTMLResponse
from lnbits.core.models import User
from lnbits.decorators import check_user_exists
from lnbits.settings import (
LNBITS_CUSTOM_LOGO,
LNBITS_SITE_TITLE,
)
from . import tpos_ext, tpos_renderer
from .crud import get_tpos
@ -33,3 +37,40 @@ async def tpos(request: Request, tpos_id):
return tpos_renderer().TemplateResponse(
"tpos/tpos.html", {"request": request, "tpos": tpos}
)
@tpos_ext.get("/manifest/{tpos_id}.webmanifest")
async def manifest(tpos_id: str):
tpos = await get_tpos(tpos_id)
if not tpos:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist."
)
return {
"short_name": LNBITS_SITE_TITLE,
"name": tpos.name + " - " + LNBITS_SITE_TITLE,
"icons": [
{
"src": LNBITS_CUSTOM_LOGO
if LNBITS_CUSTOM_LOGO
else "https://cdn.jsdelivr.net/gh/lnbits/lnbits@0.3.0/docs/logos/lnbits.png",
"type": "image/png",
"sizes": "900x900",
}
],
"start_url": "/tpos/" + tpos_id,
"background_color": "#1F2234",
"description": "Bitcoin Lightning tPOS",
"display": "standalone",
"scope": "/tpos/" + tpos_id,
"theme_color": "#1F2234",
"shortcuts": [
{
"name": tpos.name + " - " + LNBITS_SITE_TITLE,
"short_name": tpos.name,
"description": tpos.name + " - " + LNBITS_SITE_TITLE,
"url": "/tpos/" + tpos_id,
}
],
}

View File

@ -52,7 +52,9 @@ async def api_tpos_delete(
@tpos_ext.post("/api/v1/tposs/{tpos_id}/invoices", status_code=HTTPStatus.CREATED)
async def api_tpos_create_invoice(amount: int = Query(..., ge=1), tpos_id: str = None):
async def api_tpos_create_invoice(
amount: int = Query(..., ge=1), tipAmount: int = None, tpos_id: str = None
):
tpos = await get_tpos(tpos_id)
if not tpos:
@ -65,7 +67,7 @@ async def api_tpos_create_invoice(amount: int = Query(..., ge=1), tpos_id: str =
wallet_id=tpos.wallet,
amount=amount,
memo=f"{tpos.name}",
extra={"tag": "tpos"},
extra={"tag": "tpos", "tipAmount": tipAmount, "tposId": tpos_id},
)
except Exception as e:
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
@ -84,6 +86,7 @@ async def api_tpos_check_invoice(tpos_id: str, payment_hash: str):
)
try:
status = await api_payment(payment_hash)
except Exception as exc:
print(exc)
return {"paid": False}

View File

@ -37,7 +37,7 @@
<code>[&lt;wallets_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/wallet -H "X-Api-Key: {{
>curl -X GET {{ request.base_url }}watchonly/api/v1/wallet -H "X-Api-Key: {{
user.wallets[0].inkey }}"
</code>
</q-card-section>
@ -66,7 +66,7 @@
<code>[&lt;wallet_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/wallet/&lt;wallet_id&gt;
>curl -X GET {{ request.base_url }}watchonly/api/v1/wallet/&lt;wallet_id&gt;
-H "X-Api-Key: {{ user.wallets[0].inkey }}"
</code>
</q-card-section>
@ -89,7 +89,7 @@
<code>[&lt;wallet_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/wallet -d '{"title":
>curl -X POST {{ request.base_url }}watchonly/api/v1/wallet -d '{"title":
&lt;string&gt;, "masterpub": &lt;string&gt;}' -H "Content-type:
application/json" -H "X-Api-Key: {{ user.wallets[0].adminkey }}"
</code>
@ -116,7 +116,7 @@
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X DELETE {{ request.base_url
}}api/v1/wallet/&lt;wallet_id&gt; -H "X-Api-Key: {{
}}watchonly/api/v1/wallet/&lt;wallet_id&gt; -H "X-Api-Key: {{
user.wallets[0].adminkey }}"
</code>
</q-card-section>
@ -142,7 +142,7 @@
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X GET {{ request.base_url
}}api/v1/addresses/&lt;wallet_id&gt; -H "X-Api-Key: {{
}}watchonly/api/v1/addresses/&lt;wallet_id&gt; -H "X-Api-Key: {{
user.wallets[0].inkey }}"
</code>
</q-card-section>
@ -173,7 +173,7 @@
<code>[&lt;address_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/address/&lt;wallet_id&gt;
>curl -X GET {{ request.base_url }}watchonly/api/v1/address/&lt;wallet_id&gt;
-H "X-Api-Key: {{ user.wallets[0].inkey }}"
</code>
</q-card-section>
@ -202,7 +202,7 @@
<code>[&lt;mempool_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/mempool -H "X-Api-Key: {{
>curl -X GET {{ request.base_url }}watchonly/api/v1/mempool -H "X-Api-Key: {{
user.wallets[0].adminkey }}"
</code>
</q-card-section>
@ -233,7 +233,7 @@
<code>[&lt;mempool_object&gt;, ...]</code>
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X PUT {{ request.base_url }}api/v1/mempool -d '{"endpoint":
>curl -X PUT {{ request.base_url }}watchonly/api/v1/mempool -d '{"endpoint":
&lt;string&gt;}' -H "Content-type: application/json" -H "X-Api-Key:
{{ user.wallets[0].adminkey }}"
</code>

View File

@ -140,4 +140,10 @@ video {
// text-wrap
.text-wrap {
word-break: break-word;
}
.q-card {
code {
overflow-wrap: break-word;
}
}