mirror of
https://github.com/lnbits/lnbits.git
synced 2025-06-27 09:11:57 +02:00
Merge branch 'main' of github.com:lnbits/lnbits-legend
This commit is contained in:
commit
51dac0275c
3
.github/workflows/mypy.yml
vendored
3
.github/workflows/mypy.yml
vendored
@ -5,10 +5,9 @@ on: [push, pull_request]
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ 'false' == 'true' }} # skip mypy for now
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: jpetrucciani/mypy-check@master
|
||||
with:
|
||||
mypy_flags: '--install-types --non-interactive'
|
||||
path: lnbits
|
||||
path: 'lnbits'
|
||||
|
8
.github/workflows/regtest.yml
vendored
8
.github/workflows/regtest.yml
vendored
@ -16,7 +16,6 @@ jobs:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Setup Regtest
|
||||
run: |
|
||||
docker build -t lnbits-legend .
|
||||
git clone https://github.com/lnbits/legend-regtest-enviroment.git docker
|
||||
cd docker
|
||||
chmod +x ./tests
|
||||
@ -39,8 +38,8 @@ jobs:
|
||||
LNBITS_DATA_FOLDER: ./data
|
||||
LNBITS_BACKEND_WALLET_CLASS: LndRestWallet
|
||||
LND_REST_ENDPOINT: https://localhost:8081/
|
||||
LND_REST_CERT: docker/data/lnd-1/tls.cert
|
||||
LND_REST_MACAROON: docker/data/lnd-1/data/chain/bitcoin/regtest/admin.macaroon
|
||||
LND_REST_CERT: ./docker/data/lnd-1/tls.cert
|
||||
LND_REST_MACAROON: ./docker/data/lnd-1/data/chain/bitcoin/regtest/admin.macaroon
|
||||
run: |
|
||||
sudo chmod -R a+rwx . && rm -rf ./data && mkdir -p ./data
|
||||
make test-real-wallet
|
||||
@ -57,7 +56,6 @@ jobs:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Setup Regtest
|
||||
run: |
|
||||
docker build -t lnbits-legend .
|
||||
git clone https://github.com/lnbits/legend-regtest-enviroment.git docker
|
||||
cd docker
|
||||
chmod +x ./tests
|
||||
@ -79,7 +77,7 @@ jobs:
|
||||
PORT: 5123
|
||||
LNBITS_DATA_FOLDER: ./data
|
||||
LNBITS_BACKEND_WALLET_CLASS: CLightningWallet
|
||||
CLIGHTNING_RPC: docker/data/clightning-1/regtest/lightning-rpc
|
||||
CLIGHTNING_RPC: ./docker/data/clightning-1/regtest/lightning-rpc
|
||||
run: |
|
||||
sudo chmod -R a+rwx . && rm -rf ./data && mkdir -p ./data
|
||||
make test-real-wallet
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -35,3 +35,6 @@ coverage.xml
|
||||
node_modules
|
||||
lnbits/static/bundle.*
|
||||
docker
|
||||
|
||||
# Nix
|
||||
*result*
|
||||
|
12
Pipfile
12
Pipfile
@ -36,9 +36,11 @@ pycryptodomex = "*"
|
||||
|
||||
[dev-packages]
|
||||
black = "==20.8b1"
|
||||
pytest = "*"
|
||||
pytest-cov = "*"
|
||||
mypy = "*"
|
||||
pytest-asyncio = "*"
|
||||
requests = "*"
|
||||
mock = "*"
|
||||
mypy = "*"
|
||||
pytest = "*"
|
||||
pytest-asyncio = "*"
|
||||
pytest-cov = "*"
|
||||
requests = "*"
|
||||
types-mock = "*"
|
||||
types-protobuf = "*"
|
||||
|
225
Pipfile.lock
generated
225
Pipfile.lock
generated
@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "503e9942306106e40621c59f37a3ab866b483f8c5f27b879c1c6783dca30949f"
|
||||
"sha256": "08e53fc6f1fcc021b33f9b2c5ae3bd6dbde815d4b317e9341ab02cf5b625acbc"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
@ -29,6 +29,7 @@
|
||||
"sha256:413adf95f93886e442aea925f3ee43baa5a765a64a0f52c6081894f9992fdd0b",
|
||||
"sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be"
|
||||
],
|
||||
"markers": "python_full_version >= '3.6.2'",
|
||||
"version": "==3.6.1"
|
||||
},
|
||||
"asyncio": {
|
||||
@ -46,6 +47,7 @@
|
||||
"sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4",
|
||||
"sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==21.4.0"
|
||||
},
|
||||
"bech32": {
|
||||
@ -53,6 +55,7 @@
|
||||
"sha256:7d6db8214603bd7871fcfa6c0826ef68b85b0abd90fa21c285a9c5e21d2bd899",
|
||||
"sha256:990dc8e5a5e4feabbdf55207b5315fdd9b73db40be294a19b3752cde9e79d981"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==1.2.0"
|
||||
},
|
||||
"bitstring": {
|
||||
@ -76,6 +79,7 @@
|
||||
"sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d",
|
||||
"sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2022.6.15"
|
||||
},
|
||||
"cffi": {
|
||||
@ -139,6 +143,7 @@
|
||||
"sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e",
|
||||
"sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==8.1.3"
|
||||
},
|
||||
"ecdsa": {
|
||||
@ -177,6 +182,7 @@
|
||||
"sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6",
|
||||
"sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==0.12.0"
|
||||
},
|
||||
"httpcore": {
|
||||
@ -184,6 +190,7 @@
|
||||
"sha256:1105b8b73c025f23ff7c36468e4432226cbb959176eab66864b8e31c4ee27fa6",
|
||||
"sha256:18b68ab86a3ccf3e7dc0f43598eaddcf472b602aba29f9aa6ab85fe2ada3980b"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==0.15.0"
|
||||
},
|
||||
"httptools": {
|
||||
@ -238,6 +245,7 @@
|
||||
"sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
|
||||
"sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==3.3"
|
||||
},
|
||||
"jinja2": {
|
||||
@ -307,6 +315,7 @@
|
||||
"sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a",
|
||||
"sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.1.1"
|
||||
},
|
||||
"marshmallow": {
|
||||
@ -314,6 +323,7 @@
|
||||
"sha256:00040ab5ea0c608e8787137627a8efae97fabd60552a05dc889c888f814e75eb",
|
||||
"sha256:635fb65a3285a31a30f276f30e958070f5214c7196202caa5c7ecf28f5274bc7"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==3.17.0"
|
||||
},
|
||||
"outcome": {
|
||||
@ -321,6 +331,7 @@
|
||||
"sha256:6f82bd3de45da303cf1f771ecafa1633750a358436a8bb60e06a1ceb745d2672",
|
||||
"sha256:c4ab89a56575d6d38a05aa16daeaa333109c1f96167aba8901ab18b6b5e0f7f5"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==1.2.0"
|
||||
},
|
||||
"packaging": {
|
||||
@ -328,6 +339,7 @@
|
||||
"sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb",
|
||||
"sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==21.3"
|
||||
},
|
||||
"psycopg2-binary": {
|
||||
@ -473,6 +485,7 @@
|
||||
"sha256:f0f047e11febe5c3198ed346b507e1d010330d56ad615a7e0a89fae604065a0e",
|
||||
"sha256:fe4670cb32ea98ffbf5a1262f14c3e102cccd92b1869df3bb09538158ba90fe6"
|
||||
],
|
||||
"markers": "python_full_version >= '3.6.1'",
|
||||
"version": "==1.9.1"
|
||||
},
|
||||
"pyngrok": {
|
||||
@ -487,6 +500,7 @@
|
||||
"sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb",
|
||||
"sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"
|
||||
],
|
||||
"markers": "python_full_version >= '3.6.8'",
|
||||
"version": "==3.0.9"
|
||||
},
|
||||
"pypng": {
|
||||
@ -517,6 +531,7 @@
|
||||
"sha256:b7e3b04a59693c42c36f9ab1cc2acc46fa5df8c78e178fc33a8d4cd05c8d498f",
|
||||
"sha256:d92a187be61fe482e4fd675b6d52200e7be63a12b724abbf931a40ce4fa92938"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==0.20.0"
|
||||
},
|
||||
"pyyaml": {
|
||||
@ -562,9 +577,13 @@
|
||||
"sha256:026c0de2ee8385d1255b9c2426cd4f03fe9177ac94c09979bc601946c8493aa0",
|
||||
"sha256:99142650756ef1998ce0661568f54a47dac8c638fb27e3816c02536575dbba8c"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.6.0.post0"
|
||||
},
|
||||
"rfc3986": {
|
||||
"extras": [
|
||||
"idna2008"
|
||||
],
|
||||
"hashes": [
|
||||
"sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835",
|
||||
"sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"
|
||||
@ -600,6 +619,14 @@
|
||||
"index": "pypi",
|
||||
"version": "==0.14.0"
|
||||
},
|
||||
"setuptools": {
|
||||
"hashes": [
|
||||
"sha256:0d33c374d41c7863419fc8f6c10bfe25b7b498aa34164d135c622e52580c6b16",
|
||||
"sha256:c04b44a57a6265fe34a4a444e965884716d34bae963119a76353434d6f18e450"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==63.2.0"
|
||||
},
|
||||
"shortuuid": {
|
||||
"hashes": [
|
||||
"sha256:459f12fa1acc34ff213b1371467c0325169645a31ed989e268872339af7563d5",
|
||||
@ -613,6 +640,7 @@
|
||||
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
|
||||
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'",
|
||||
"version": "==1.16.0"
|
||||
},
|
||||
"sniffio": {
|
||||
@ -620,6 +648,7 @@
|
||||
"sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663",
|
||||
"sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==1.2.0"
|
||||
},
|
||||
"sqlalchemy": {
|
||||
@ -676,17 +705,18 @@
|
||||
},
|
||||
"sse-starlette": {
|
||||
"hashes": [
|
||||
"sha256:840607fed361360cc76f8408a25f0eca887e7cab3c3ee44f9762f179428e2bd4",
|
||||
"sha256:ca2de945af80b83f1efda6144df9e13db83880b3b87c660044b64f199395e8b7"
|
||||
"sha256:14608559d40e3e7c6385e8c5a7b88468f7fc40c2277673a1fe8d26568e8d7c65",
|
||||
"sha256:72438ed39b1612d1ea6d89a7c0af8afee6de0389dcbe2e77539001e78b5aa89c"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.10.3"
|
||||
"version": "==1.0.0"
|
||||
},
|
||||
"starlette": {
|
||||
"hashes": [
|
||||
"sha256:5a60c5c2d051f3a8eb546136aa0c9399773a689595e099e0877704d5888279bf",
|
||||
"sha256:c6d21096774ecb9639acad41b86b7706e52ba3bf1dc13ea4ed9ad593d47e24c7"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==0.19.1"
|
||||
},
|
||||
"typing-extensions": {
|
||||
@ -698,6 +728,9 @@
|
||||
"version": "==4.3.0"
|
||||
},
|
||||
"uvicorn": {
|
||||
"extras": [
|
||||
"standard"
|
||||
],
|
||||
"hashes": [
|
||||
"sha256:c19a057deb1c5bb060946e2e5c262fc01590c6529c0af2c3d9ce941e89bc30e0",
|
||||
"sha256:cade07c403c397f9fe275492a48c1b869efd175d5d8a692df649e6e7e2ed8f4e"
|
||||
@ -815,6 +848,7 @@
|
||||
"sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4",
|
||||
"sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==21.4.0"
|
||||
},
|
||||
"black": {
|
||||
@ -829,6 +863,7 @@
|
||||
"sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d",
|
||||
"sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2022.6.15"
|
||||
},
|
||||
"charset-normalizer": {
|
||||
@ -836,6 +871,7 @@
|
||||
"sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5",
|
||||
"sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2.1.0"
|
||||
},
|
||||
"click": {
|
||||
@ -843,9 +879,13 @@
|
||||
"sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e",
|
||||
"sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==8.1.3"
|
||||
},
|
||||
"coverage": {
|
||||
"extras": [
|
||||
"toml"
|
||||
],
|
||||
"hashes": [
|
||||
"sha256:0895ea6e6f7f9939166cc835df8fa4599e2d9b759b02d1521b574e13b859ac32",
|
||||
"sha256:0f211df2cba951ffcae210ee00e54921ab42e2b64e0bf2c0befc977377fb09b7",
|
||||
@ -889,6 +929,7 @@
|
||||
"sha256:f23876b018dfa5d3e98e96f5644b109090f16a4acb22064e0f06933663005d39",
|
||||
"sha256:f7bd0ffbcd03dc39490a1f40b2669cc414fae0c4e16b77bb26806a4d0b7d1452"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==6.4.2"
|
||||
},
|
||||
"idna": {
|
||||
@ -896,6 +937,7 @@
|
||||
"sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
|
||||
"sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==3.3"
|
||||
},
|
||||
"iniconfig": {
|
||||
@ -954,6 +996,7 @@
|
||||
"sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb",
|
||||
"sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==21.3"
|
||||
},
|
||||
"pathspec": {
|
||||
@ -968,6 +1011,7 @@
|
||||
"sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159",
|
||||
"sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==1.0.0"
|
||||
},
|
||||
"py": {
|
||||
@ -975,6 +1019,7 @@
|
||||
"sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719",
|
||||
"sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==1.11.0"
|
||||
},
|
||||
"pyparsing": {
|
||||
@ -982,6 +1027,7 @@
|
||||
"sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb",
|
||||
"sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"
|
||||
],
|
||||
"markers": "python_full_version >= '3.6.8'",
|
||||
"version": "==3.0.9"
|
||||
},
|
||||
"pytest": {
|
||||
@ -1010,82 +1056,83 @@
|
||||
},
|
||||
"regex": {
|
||||
"hashes": [
|
||||
"sha256:00d2e907d3c5e4f85197c8d2263a9cc2d34bf234a9c6236ae42a3fb0bc09b759",
|
||||
"sha256:0186edcda692c38381db8ac257c2d023fd2e08818d45dc5bee4ed84212045f51",
|
||||
"sha256:06c509bd7dcb7966bdb03974457d548e54d8327bad5b0c917e87248edc43e2eb",
|
||||
"sha256:0a3f3f45c5902eb4d90266002ccb035531ae9b9278f6d5e8028247c34d192099",
|
||||
"sha256:0c1821146b429e6fdbd13ea10f26765e48d5284bc79749468cfbfe3ceb929f0d",
|
||||
"sha256:0d93167b7d7731fa9ff9fdc1bae84ec9c7133b01a35f8cc04e926d48da6ce1f7",
|
||||
"sha256:0fd8c3635fa03ef79d07c7b3ed693b3f3930ccb52c0c51761c3296a7525b135c",
|
||||
"sha256:119091c675e6ad19da8770f89aa1d52f4ad2a2018d631956f3e90c45882df880",
|
||||
"sha256:121981ba84309dabefd5e1debd49be6d51624e54b4d44bfc184cd8d555ff1df1",
|
||||
"sha256:1244e9b9b4b81c9c34e8a84273ffaeebdc78abc98a5b02dcdd49845eb3c63bd7",
|
||||
"sha256:12e1404dfb4e928d3273a10e3468877fe84bdcd3c50b655a2c9613cfc5d9fe63",
|
||||
"sha256:13d74951c14708f00700bb29475129ecbc40e01b4029c62ee7bfe9d1f59f31ce",
|
||||
"sha256:162a5939a6fdf48658d3565eeff35acdd207e07367bf5caaff3d9ea7cb77d7a9",
|
||||
"sha256:1703490c5b850fa9cef1af00c58966756042e6ca22f4fb5bb857345cd535834f",
|
||||
"sha256:18e6203cfd81df42a987175aaeed7ba46bcb42130cd81763e2d5edcff0006d5d",
|
||||
"sha256:192c2784833aea6fc7b004730bf1b91b8b8c6b998b30271aaf3bd8adfef20a96",
|
||||
"sha256:1948d3ceac5b2d55bc93159c1e0679a256a87a54c735be5cef4543a9e692dbb9",
|
||||
"sha256:206a327e628bc529d64b21ff79a5e2564f5aec7dc7abcd4b2e8a4b271ec10550",
|
||||
"sha256:2e5db20412f0db8798ff72473d16da5f13ec808e975b49188badb2462f529fa9",
|
||||
"sha256:2f94b0befc811fe74a972b1739fffbf74c0dc1a91102aca8e324aa4f2c6991bd",
|
||||
"sha256:303676797c4c7978726e74eb8255d68f7125a3a29da71ff453448f2117290e9a",
|
||||
"sha256:34ae4f35db30caa4caf85c55069fcb7a05966a3a5ba6e9e1dab5477d84fbb08a",
|
||||
"sha256:3c6df8be7d1dd35a0d9a200fbc29f888c4452c8882d284f87608046152e049e6",
|
||||
"sha256:402fa998c5988d11ed34585eb65740dcebd0fd11844d12eb0a6b4be178eb9c64",
|
||||
"sha256:40a28759d345c0bb1f5b0ac74ac04f5d48136019522c95c0ec4b07786f67ce20",
|
||||
"sha256:414ae507ba88264444baf771fec43ce0adcd4c5dbb304d3e0716f3f4d4499d2e",
|
||||
"sha256:42da079e31ae9818ffa7a35cdd16ab7104e3f7eca9c0958040aede827b2e55c6",
|
||||
"sha256:473a7d21932ce7c314953b33c32e63df690181860edcdf14bba1278cdf71b07f",
|
||||
"sha256:49fcb45931a693b0e901972c5e077ea2cf30ec39da699645c43cb8b1542c6e14",
|
||||
"sha256:4c5913cb9769038bd03e42318955c2f15a688384a6a0b807bcfc8271603d9277",
|
||||
"sha256:4cfeb71095c8d8380a5df5a38ff94d27a3f483717e509130a822b4d6400b7991",
|
||||
"sha256:4dc74f0171eede67d79a79c06eca0fe5b7b280dbb8c27ad1fae4ced2ad66268f",
|
||||
"sha256:5b1cffff2d9f832288fe516021cb81c95c57c0067b13a82f1d2daabdbc2f4270",
|
||||
"sha256:601c99ac775b6c89699a48976f3dbb000b47d3ca59362c8abc9582e6d0780d91",
|
||||
"sha256:667a06bb8d72b6da3d9cf38dac4ba969688868ed2279a692e993d2c0e1c30aba",
|
||||
"sha256:673549a0136c7893f567ed71ab5225ed3701c79b17c0a7faee846c645fc24010",
|
||||
"sha256:67bd3bdd27db7a6460384869dd4b9c54267d805b67d70b20495bb5767f8e051c",
|
||||
"sha256:727edff0a4eaff3b6d26cbb50216feac9055aba7e6290eec23c061c2fe2fab55",
|
||||
"sha256:782627a1cb8fbb1c78d8e841f5b71c2c683086c038f975bebdac7cce7678a96f",
|
||||
"sha256:7d462ba84655abeddae4dfc517fe1afefb5430b3b5acb0a954de12a47aea7183",
|
||||
"sha256:8ab39aa445d00902c43a1e951871bedc7f18d095a21eccba153d594faac34aea",
|
||||
"sha256:8e2075ed4ea2e231e2e98b16cfa5dae87e9a6045a71104525e1efc29aa8faa8e",
|
||||
"sha256:9daeccb2764bf4cc280c40c6411ae176bb0876948e536590a052b3d647254c95",
|
||||
"sha256:9e4006942334fa954ebd32fa0728718ec870f95f4ba7cda9edc46dd49c294f22",
|
||||
"sha256:9f1c8fffd4def0b76c0947b8cb261b266e31041785dc2dc2db7569407a2f54fe",
|
||||
"sha256:a00cd58a30a1041c193777cb1bc090200b05ff4b073d5935738afd1023e63069",
|
||||
"sha256:a0220a7a16fd4bfc700661f920510defd31ef7830ce992d5cc51777aa8ccd724",
|
||||
"sha256:a048f91823862270905cb22ef88038b08aac852ce48e0ecc4b4bf1b895ec37d9",
|
||||
"sha256:a3c47c71fde0c5d584402e67546c81af9951540f1f622d821e9c20761556473a",
|
||||
"sha256:a6d9ea727fd1233ee746bf44dd37e7d4320b3ed8ff09e73d7638c969b28d280f",
|
||||
"sha256:ab0709daedc1099bbd4371ae17eeedd4efc1cf70fcdcfe5de1374a0944b61f80",
|
||||
"sha256:ab1cb36b411f16da6e057ef8e6657dd0af36f59a667f07e0b4b617e44e53d7b2",
|
||||
"sha256:ae1c5b435d44aa91d48cc710f20c3485e0584a3ad3565d5ae031d61a35f674f4",
|
||||
"sha256:b279b9bb401af41130fd2a09427105100bc8c624ed45da1c81c1c0d0aa639734",
|
||||
"sha256:b72a4ec79a15f6066d14ae1c472b743af4b4ecee14420e8d6e4a336b49b8f21c",
|
||||
"sha256:c2cd93725911c0159d597b90c96151070ef7e0e67604637e2f2abe06c34bf079",
|
||||
"sha256:c7c5f914b0eb5242c09f91058b80295525897e873b522575ab235b48db125597",
|
||||
"sha256:d07d849c9e2eca80adb85d3567302a47195a603ad7b1f0a07508e253c041f954",
|
||||
"sha256:d2672d68cf6c8452b6758fc3cd2d8feac966d511eed79a68182a5297b473af9c",
|
||||
"sha256:d35bbcbf70d14f724e7489746cf68efe122796578addd98f91428e144d0ad266",
|
||||
"sha256:d40b4447784dbe0896a6d10a178f6724598161f942c56f5a60dc0ef7fe63f7a1",
|
||||
"sha256:d561dcb0fb0ab858291837d51330696a45fd3ba6912a332a4ee130e5484b9e47",
|
||||
"sha256:d7f5ccfff648093152cadf6d886c7bd922047532f72024c953a79c7553aac2fe",
|
||||
"sha256:dce6b2ad817e3eb107f8704782b091b0631dd3adf47f14bdc086165d05b528b0",
|
||||
"sha256:e1fdda3ec7e9785065b67941693995cab95b54023a21db9bf39e54cc7b2c3526",
|
||||
"sha256:e2a262ec85c595fc8e1f3162cafc654d2219125c00ea3a190c173cea70d2cc7a",
|
||||
"sha256:e2fc1e3928c1189c0382c547c17717c6d9f425fffe619ef94270fe4c6c8be0a6",
|
||||
"sha256:ea27acd97a752cfefa9907da935e583efecb302e6e9866f37565968c8407ad58",
|
||||
"sha256:ee769a438827e443ed428e66d0aa7131c653ecd86ddc5d4644a81ed1d93af0e7",
|
||||
"sha256:f32e0d1c7e7b0b9c3cac76f3d278e7ee6b99c95672d2c1c6ea625033431837c0",
|
||||
"sha256:f355caec5bbce20421dc26e53787b10e32fd0df68db2b795435217210c08d69c",
|
||||
"sha256:f87e9108bb532f8a1fc6bf7e69b930a35c7b0267b8fef0a3ede0bcb4c5aaa531",
|
||||
"sha256:f8a2fd2f62a77536e4e3193303bec380df40d99e253b1c8f9b6eafa07eaeff67",
|
||||
"sha256:fbdf4fc6adf38fab1091c579ece3fe9f493bd0f1cfc3d2c76d2e52461ca4f8a9"
|
||||
"sha256:045682b6457f0224deb10c9720b8008dc798b1ad4331de9302fd4b615211e5a5",
|
||||
"sha256:04e3869fc6fc24b75d38e7547797bb0a82d6cccd49e8ce6ae21a0b87aeb9fac7",
|
||||
"sha256:073bd76a1f03e05a6ca0df705b6117f75b10c340af068b55becb1334fc6426c2",
|
||||
"sha256:0aa48740a1385cb668ffd828a7e2c8078ce29c72d64651c8226bd2b7cb5dba0c",
|
||||
"sha256:0e380ebd841201f980ab022d07033be12c9438a9b2e0f60324c9e3ba31790918",
|
||||
"sha256:0f9b8ef2e46d627b2a85d3f4fe433ead283c420adcf9461906c3db10766dd3b1",
|
||||
"sha256:10d3a5dda21a125cfdc31e45ae6ce6bdd1e45cb194199801248d6580be8dc337",
|
||||
"sha256:11965ae779a0ccfb6d17996d531e2f522124e04d98cc65742b96bf8f50758ab9",
|
||||
"sha256:1c61175730596266015c4e005c65cbfeeb1ef019ccd024870169c6593a26bdb0",
|
||||
"sha256:1d1d79a87a33fbd6573e30eb53969a4190035894c4390a0787fb823e9e86b72f",
|
||||
"sha256:1f0bf228c948f543876f4fb310322a4ff7e398667dd58aeb4815dc9e30bf867e",
|
||||
"sha256:2026a1c108752e48577f9720076bf6e31a60aaf0c3000fffad4e2527fdffbe95",
|
||||
"sha256:204705b7ec16267d39870a19e72e832b12739dc48a26d923a9cb94043660d50f",
|
||||
"sha256:248b16534d1ef8f10a72cda0f97b3dfa25b3d9123a7e726d1594cb07a541bba0",
|
||||
"sha256:27b5011449213dfd880e592ea6d311d00739e87d9512bad507ee18c9c92a20ac",
|
||||
"sha256:2880d21e9507869ab1636e50461afb9ffb08797f1cb76f70d3ad52e7dd13a335",
|
||||
"sha256:2d3f9fc885ecd8b0eb248d0e190aa7264b977cc23b6da7c08444065170c57e2e",
|
||||
"sha256:2d8b50ae3cbaaa2e5ed89ed81fc025ec64b1a54c4f34e6bdaad9dc63fc2afa6b",
|
||||
"sha256:3ff5b2b6a136307a5551e7821d83ab12c46f57c32bf23a27877c9c6bdb55aa61",
|
||||
"sha256:40b4436466d47271fe3b4df63e55307c91a40cda0875a9ff3b7231a08394b283",
|
||||
"sha256:435c94d5939a7cf4b0af1cce30d196451bae441ebe64f63d08517ab490ceb385",
|
||||
"sha256:4c0b7c413d4a8a55d72df18acbeb50276eee19cec7e2f54ed3bddc46bc3b3aeb",
|
||||
"sha256:4cfe87490d0a801749b42491ef7e968342e5787decbf57d5402fc2c17f7302e3",
|
||||
"sha256:4f480661cd0809a1177b09581c12c9ceff9ca989e4a0c8c0f10379dffa3b4c4c",
|
||||
"sha256:53370620db6058dbb464324b053fee8608518d76ff6352b2835d71e2ae8ef293",
|
||||
"sha256:53eca0cece2ddb592b8dd9746f0b258d0c8a45f5a3ac8eb96833058f64778fca",
|
||||
"sha256:5751bfe0d939d7110396510a39e48ee928b36b55177207c47766b093886a3945",
|
||||
"sha256:5d750f99c40a7e994df1cf1295bbb3e873417ca69508664fe9f65db92e46ca40",
|
||||
"sha256:6019737db5c46a24f307eae5069fced0752e3a22380398bbefbef77b068b9537",
|
||||
"sha256:605ca47681c7405723a4970d66d13fa3a3a66efa6b8499d7ab7bce1ddb44a36f",
|
||||
"sha256:69120a8fb1eb932b6e3ededb16448be6444eb952f9350c21dddbef947fea5690",
|
||||
"sha256:72713de336b8d895f91aad34f5591f33d1d8727bd739af3ae2657411ef6e0739",
|
||||
"sha256:74077b462a9b255c5fca247484f46b0f25c32676fe4645cb6b5304b2d997357a",
|
||||
"sha256:74f0067495a842f7cc198b14031a2893d377bed38e19d785f35095082ab5a556",
|
||||
"sha256:817a0618c149d77e493963cd98851ff49d6ab8bcab247fdbf85bb89a14dca5c3",
|
||||
"sha256:869a0f6405ec569863e09909617138af575b5e2bc5181184e60f339a4c8a6d7a",
|
||||
"sha256:86b0cf786efd587c27abd1d07020c555a82275bba3506d916d42aed7a3744967",
|
||||
"sha256:8cbc407c44003a1cb4aaa2d48cc19b45dde07ba0ae2f541c6750ad18f8c118f5",
|
||||
"sha256:90e082f262cf858cbcde330999ea5612e12918982033b716d2c5d8b1bc7a01db",
|
||||
"sha256:95d8f1083ff4546ec14fb46dc41b042d372258f8c319df1e2316d8fe1bd3f085",
|
||||
"sha256:97211bae1bc51f153764485a54d8d1130196cae33d02285c33732b26c5328b8d",
|
||||
"sha256:98dbaf6a86991e2b09f4d8a7669b4304755bda519565971dc3b87ee00fd6eab2",
|
||||
"sha256:9e297ff33172853e9a9e46dbb0c2ebf44fb38ebefce659698df4eb9dfce0a748",
|
||||
"sha256:9e64492c8105312f080e25e457db70f9b0d02e6ba3c1ea14468087b0e3aa876f",
|
||||
"sha256:a11241808e59deec8314792bbd8a6f0a8a7a95b742709e134c73a3216dbb26ae",
|
||||
"sha256:a33c36dcc1760d66f1969d7d3dc8956f45a3d502178053074b8489f67718138f",
|
||||
"sha256:a4e1e7ba8c58c1f0b828418f9a2635c0f6344bf107308b8fe65f234a13c8462a",
|
||||
"sha256:a55c4d5e5076cc5ece625dd1f7015c9a0818ba1f9ad9db421b495d7ece088e56",
|
||||
"sha256:a6f5cc82e1fa380eb6b8040d626df6ba9f492b6886527f53d59838b11e9caaef",
|
||||
"sha256:a96826b5c9dc68417ddb29843998473f9c2c047911e6fca36a9f81a898087b01",
|
||||
"sha256:acf6cbcc19d86f44e8a9d3cf1f6946a71dc55c2ec8ae374c547b1eefd83b954f",
|
||||
"sha256:b4be25d3c640a35671431d8ef8cd522319254074b150147fcacad90c91ea42c2",
|
||||
"sha256:b5a31abc27f9bda7a455bfa1e1bd623da50e3a343a040084c879d07394a93481",
|
||||
"sha256:bb812e590e3881a93d4d291270440b3795fa4c0bc1b03ba15fe1cc88d2ea4347",
|
||||
"sha256:bbd542b4afd2ed5491a480e9b15f4bca13e0170ef1895064fd15741382fdebe0",
|
||||
"sha256:bf27111f56b762238bd3ce4c5e8ad34167d85dd3c077721a0c093517526a94af",
|
||||
"sha256:c7a4dc9436b7a55c36daa3959e92d70337c547651ebeda685dd6bb083f0b77ab",
|
||||
"sha256:c88a1068ac8e5dc579d5104903fa2c488448c1137e580a77d1438d98070c4243",
|
||||
"sha256:c8cffb5040bf432355cfe51378072f20087609694066ace80710bdba04cf9ce2",
|
||||
"sha256:c944921b0e77f1923dd89cda65534223ac107e24d71b1dfe174237faa5efd32a",
|
||||
"sha256:ca2a7233275acf0087ecd15e5fb0eeb722a1f4de453b49bb1443edf2c2f5a997",
|
||||
"sha256:cf0a3b9744f94693f3ebcca1c259354f0043c19a4ce938f80ad6d1816b8fd8f0",
|
||||
"sha256:dbe2f16a66f64a00dc9ccc0db7f8b5ff014f409840e03675eb431f03b50ddffd",
|
||||
"sha256:deb7b067b3b9751c60dc7f6de68476138d550c074a5016ba944cc55863fa86d1",
|
||||
"sha256:df669bacbda209e9b00928f1d00432b27a16c3e051f9f7e5ea306f9b78bf3e7c",
|
||||
"sha256:e2dd4ca82c2241be9582d2ae060070f2bccb0c98295b608009d5cc6e6041eaed",
|
||||
"sha256:e432cf909c53506da4c8308753b2671ee37d2d8d1de8b4b54ab76e91ca7ba0b5",
|
||||
"sha256:e4e7f1aba3aaf08e11d33fd5c2d8dd8cbf573049474e11256c91e3ba3d5e1642",
|
||||
"sha256:e51ab7fbfe5ac3002b9aee527bcb164b17fd92f5663ebf2a4e5917dd9d577864",
|
||||
"sha256:ea00c7f86405d88995e7bab5609e343fdedfe1ffc8191d3b5ed0f8c7f5eb17ec",
|
||||
"sha256:f7006d7c74e25f8bc592604a5a72ba624f10ebd5c0683ab4d3e940a88ac0098c",
|
||||
"sha256:f93e3e5acf82812ea92a1ccdcce690aab18c4044dd824f6b959d2b6069d84312",
|
||||
"sha256:fa8a4bc81b15f49c57ede3fd636786c6619179661acf2430fcc387d75bf28d33",
|
||||
"sha256:fc44c49f33dd75e58b5ff2a5ac50c96c84b6b209d36b4790c85bca08a3b9017d"
|
||||
],
|
||||
"version": "==2022.7.9"
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2022.7.24"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
@ -1100,6 +1147,7 @@
|
||||
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
|
||||
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
|
||||
],
|
||||
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'",
|
||||
"version": "==0.10.2"
|
||||
},
|
||||
"tomli": {
|
||||
@ -1107,6 +1155,7 @@
|
||||
"sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
|
||||
"sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.0.1"
|
||||
},
|
||||
"typed-ast": {
|
||||
@ -1136,8 +1185,25 @@
|
||||
"sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3",
|
||||
"sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==1.5.4"
|
||||
},
|
||||
"types-mock": {
|
||||
"hashes": [
|
||||
"sha256:4535fbb3912b88a247d43cdb41db0c8b2e187138986f6f01a989717e56105848",
|
||||
"sha256:a849bc2d966063f4946013bf404822ee2b96f77a8dccda4174b70ab61c5293fe"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.0.15"
|
||||
},
|
||||
"types-protobuf": {
|
||||
"hashes": [
|
||||
"sha256:d291388678af91bb045fafa864f142dc4ac22f5d4cdca097c7d8d8a32fa9b3ab",
|
||||
"sha256:d2b26861b0cb46a3c8669b0df507b7ef72e487da66d61f9f3576aa76ce028a83"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.19.22"
|
||||
},
|
||||
"typing-extensions": {
|
||||
"hashes": [
|
||||
"sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02",
|
||||
@ -1151,6 +1217,7 @@
|
||||
"sha256:8298d6d56d39be0e3bc13c1c97d133f9b45d797169a0e11cdd0e0489d786f7ec",
|
||||
"sha256:879ba4d1e89654d9769ce13121e0f94310ea32e8d2f8cf587b77c08bbcdb30d6"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'",
|
||||
"version": "==1.26.10"
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ LNbits
|
||||
|
||||

|
||||
|
||||
# LNbits v0.3 BETA, free and open-source lightning-network wallet/accounts system
|
||||
# LNbits v0.9 BETA, free and open-source lightning-network wallet/accounts system
|
||||
|
||||
(Join us on [https://t.me/lnbits](https://t.me/lnbits))
|
||||
|
||||
|
110
build.py
Normal file
110
build.py
Normal file
@ -0,0 +1,110 @@
|
||||
import warnings
|
||||
import subprocess
|
||||
import glob
|
||||
import os
|
||||
from os import path
|
||||
from typing import Any, List, NamedTuple, Optional
|
||||
from pathlib import Path
|
||||
|
||||
LNBITS_PATH = path.dirname(path.realpath(__file__)) + "/lnbits"
|
||||
|
||||
def get_js_vendored(prefer_minified: bool = False) -> List[str]:
|
||||
paths = get_vendored(".js", prefer_minified)
|
||||
|
||||
def sorter(key: str):
|
||||
if "moment@" in key:
|
||||
return 1
|
||||
if "vue@" in key:
|
||||
return 2
|
||||
if "vue-router@" in key:
|
||||
return 3
|
||||
if "polyfills" in key:
|
||||
return 4
|
||||
return 9
|
||||
|
||||
return sorted(paths, key=sorter)
|
||||
|
||||
|
||||
def get_css_vendored(prefer_minified: bool = False) -> List[str]:
|
||||
paths = get_vendored(".css", prefer_minified)
|
||||
|
||||
def sorter(key: str):
|
||||
if "quasar@" in key:
|
||||
return 1
|
||||
if "vue@" in key:
|
||||
return 2
|
||||
if "chart.js@" in key:
|
||||
return 100
|
||||
return 9
|
||||
|
||||
return sorted(paths, key=sorter)
|
||||
|
||||
|
||||
def get_vendored(ext: str, prefer_minified: bool = False) -> List[str]:
|
||||
paths: List[str] = []
|
||||
for path in glob.glob(
|
||||
os.path.join(LNBITS_PATH, "static/vendor/**"), recursive=True
|
||||
):
|
||||
if path.endswith(".min" + ext):
|
||||
# path is minified
|
||||
unminified = path.replace(".min" + ext, ext)
|
||||
if prefer_minified:
|
||||
paths.append(path)
|
||||
if unminified in paths:
|
||||
paths.remove(unminified)
|
||||
elif unminified not in paths:
|
||||
paths.append(path)
|
||||
|
||||
elif path.endswith(ext):
|
||||
# path is not minified
|
||||
minified = path.replace(ext, ".min" + ext)
|
||||
if not prefer_minified:
|
||||
paths.append(path)
|
||||
if minified in paths:
|
||||
paths.remove(minified)
|
||||
elif minified not in paths:
|
||||
paths.append(path)
|
||||
|
||||
return sorted(paths)
|
||||
|
||||
|
||||
def url_for_vendored(abspath: str) -> str:
|
||||
return "/" + os.path.relpath(abspath, LNBITS_PATH)
|
||||
|
||||
def transpile_scss():
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
from scss.compiler import compile_string # type: ignore
|
||||
|
||||
with open(os.path.join(LNBITS_PATH, "static/scss/base.scss")) as scss:
|
||||
with open(os.path.join(LNBITS_PATH, "static/css/base.css"), "w") as css:
|
||||
css.write(compile_string(scss.read()))
|
||||
|
||||
def bundle_vendored():
|
||||
for getfiles, outputpath in [
|
||||
(get_js_vendored, os.path.join(LNBITS_PATH, "static/bundle.js")),
|
||||
(get_css_vendored, os.path.join(LNBITS_PATH, "static/bundle.css")),
|
||||
]:
|
||||
output = ""
|
||||
for path in getfiles():
|
||||
with open(path) as f:
|
||||
output += "/* " + url_for_vendored(path) + " */\n" + f.read() + ";\n"
|
||||
with open(outputpath, "w") as f:
|
||||
f.write(output)
|
||||
|
||||
|
||||
def build():
|
||||
transpile_scss()
|
||||
bundle_vendored()
|
||||
# root = Path("lnbits/static/foo")
|
||||
# root.mkdir(parents=True)
|
||||
# root.joinpath("example.css").write_text("")
|
||||
|
||||
if __name__ == "__main__":
|
||||
build()
|
||||
|
||||
#def build(setup_kwargs):
|
||||
# """Build """
|
||||
# transpile_scss()
|
||||
# bundle_vendored()
|
||||
# subprocess.run(["ls", "-la", "./lnbits/static"])
|
@ -8,13 +8,32 @@ nav_order: 2
|
||||
|
||||
# Basic installation
|
||||
|
||||
You can choose between two python package managers, `venv` and `pipenv`. Both are fine but if you don't know what you're doing, just go for the first option.
|
||||
You can choose between four package managers, `poetry`, `pipenv`, `venv` and `nix`.
|
||||
|
||||
By default, LNbits will use SQLite as its database. You can also use PostgreSQL which is recommended for applications with a high load (see guide below).
|
||||
|
||||
## Option 1: pipenv
|
||||
## Option 1: poetry
|
||||
|
||||
You can also use Pipenv to manage your python packages.
|
||||
```sh
|
||||
git clone https://github.com/lnbits/lnbits-legend.git
|
||||
cd lnbits-legend/
|
||||
|
||||
curl -sSL https://install.python-poetry.org | python3 -
|
||||
poetry install
|
||||
|
||||
# You may need to install python 3.9, update your python following this guide https://linuxize.com/post/how-to-install-python-3-9-on-ubuntu-20-04/
|
||||
|
||||
mkdir data && cp .env.example .env
|
||||
```
|
||||
|
||||
#### Running the server
|
||||
|
||||
```sh
|
||||
poetry run lnbits
|
||||
# To change port/host pass 'poetry run lnbits --port 9000 --host 0.0.0.0'
|
||||
```
|
||||
|
||||
## Option 2: pipenv
|
||||
|
||||
```sh
|
||||
git clone https://github.com/lnbits/lnbits-legend.git
|
||||
@ -44,9 +63,7 @@ pipenv run python -m uvicorn lnbits.__main__:app --port 5000 --host 0.0.0.0
|
||||
Add the flag `--reload` for development (includes hot-reload).
|
||||
|
||||
|
||||
## Option 2: venv
|
||||
|
||||
Download this repo and install the dependencies:
|
||||
## Option 3: venv
|
||||
|
||||
```sh
|
||||
git clone https://github.com/lnbits/lnbits-legend.git
|
||||
@ -67,6 +84,26 @@ mkdir data && cp .env.example .env
|
||||
|
||||
If you want to host LNbits on the internet, run with the option `--host 0.0.0.0`.
|
||||
|
||||
## Option 4: Nix
|
||||
|
||||
```sh
|
||||
git clone https://github.com/lnbits/lnbits-legend.git
|
||||
cd lnbits-legend/
|
||||
# Install nix, modern debian distros usually already include
|
||||
sh <(curl -L https://nixos.org/nix/install) --daemon
|
||||
|
||||
nix build .#lnbits
|
||||
mkdir data
|
||||
|
||||
```
|
||||
|
||||
#### Running the server
|
||||
|
||||
```sh
|
||||
# .env variables are currently passed when running
|
||||
LNBITS_DATA_FOLDER=data LNBITS_BACKEND_WALLET_CLASS=LNbitsWallet LNBITS_ENDPOINT=https://legend.lnbits.com LNBITS_KEY=7b1a78d6c78f48b09a202f2dcb2d22eb ./result/bin/lnbits --port 9000
|
||||
```
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
Problems installing? These commands have helped us install LNbits.
|
||||
|
@ -17,7 +17,6 @@ A backend wallet can be configured using the following LNbits environment variab
|
||||
### CLightning
|
||||
|
||||
Using this wallet requires the installation of the `pylightning` Python package.
|
||||
If you want to use LNURLp you should use SparkWallet because of an issue with description_hash and CLightning.
|
||||
|
||||
- `LNBITS_BACKEND_WALLET_CLASS`: **CLightningWallet**
|
||||
- `CLIGHTNING_RPC`: /file/path/lightning-rpc
|
||||
|
77
flake.lock
generated
Normal file
77
flake.lock
generated
Normal file
@ -0,0 +1,77 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"locked": {
|
||||
"lastModified": 1656928814,
|
||||
"narHash": "sha256-RIFfgBuKz6Hp89yRr7+NR5tzIAbn52h8vT6vXkYjZoM=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "7e2a3b3dfd9af950a856d66b0a7d01e3c18aa249",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1657114324,
|
||||
"narHash": "sha256-fWuaUNXrHcz/ciHRHlcSO92dvV3EVS0GJQUSBO5JIB4=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a5c867d9fe9e4380452628e8f171c26b69fa9d3d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1657261001,
|
||||
"narHash": "sha256-sUZeuRYfhG59uD6xafM07bc7bAIkpcGq84Vj4B+cyms=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "0be91cefefde5701f8fa957904618a13e3bb51d8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"poetry2nix": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1657149754,
|
||||
"narHash": "sha256-iSnZoqwNDDVoO175whSuvl4sS9lAb/2zZ3Sa4ywo970=",
|
||||
"owner": "nix-community",
|
||||
"repo": "poetry2nix",
|
||||
"rev": "fc1930e011dea149db81863aac22fe701f36f1b5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "poetry2nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs",
|
||||
"poetry2nix": "poetry2nix"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
55
flake.nix
Normal file
55
flake.nix
Normal file
@ -0,0 +1,55 @@
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
poetry2nix.url = "github:nix-community/poetry2nix";
|
||||
};
|
||||
outputs = { self, nixpkgs, poetry2nix }@inputs:
|
||||
let
|
||||
supportedSystems = [ "x86_64-linux" "aarch64-linux" ];
|
||||
forSystems = systems: f:
|
||||
nixpkgs.lib.genAttrs systems
|
||||
(system: f system (import nixpkgs { inherit system; overlays = [ poetry2nix.overlay self.overlays.default ]; }));
|
||||
forAllSystems = forSystems supportedSystems;
|
||||
projectName = "lnbits";
|
||||
in
|
||||
{
|
||||
devShells = forAllSystems (system: pkgs: {
|
||||
default = pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
nodePackages.prettier
|
||||
];
|
||||
};
|
||||
});
|
||||
overlays = {
|
||||
default = final: prev: {
|
||||
${projectName} = self.packages.${final.hostPlatform.system}.${projectName};
|
||||
};
|
||||
};
|
||||
packages = forAllSystems (system: pkgs: {
|
||||
default = self.packages.${system}.${projectName};
|
||||
${projectName} = pkgs.poetry2nix.mkPoetryApplication {
|
||||
projectDir = ./.;
|
||||
python = pkgs.python39;
|
||||
};
|
||||
});
|
||||
nixosModules = {
|
||||
default = { pkgs, lib, config, ... }: {
|
||||
imports = [ "${./nix/modules/${projectName}-service.nix}" ];
|
||||
nixpkgs.overlays = [ self.overlays.default ];
|
||||
};
|
||||
};
|
||||
checks = forAllSystems (system: pkgs:
|
||||
let
|
||||
vmTests = import ./nix/tests {
|
||||
makeTest = (import (nixpkgs + "/nixos/lib/testing-python.nix") { inherit system; }).makeTest;
|
||||
inherit inputs pkgs;
|
||||
};
|
||||
in
|
||||
pkgs.lib.optionalAttrs pkgs.stdenv.isLinux vmTests # vmTests can only be ran on Linux, so append them only if on Linux.
|
||||
//
|
||||
{
|
||||
# Other checks here...
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
@ -4,7 +4,7 @@ import uvloop
|
||||
from loguru import logger
|
||||
from starlette.requests import Request
|
||||
|
||||
from .commands import bundle_vendored, migrate_databases, transpile_scss
|
||||
from .commands import migrate_databases
|
||||
from .settings import (
|
||||
DEBUG,
|
||||
HOST,
|
||||
@ -19,8 +19,6 @@ from .settings import (
|
||||
uvloop.install()
|
||||
|
||||
asyncio.create_task(migrate_databases())
|
||||
transpile_scss()
|
||||
bundle_vendored()
|
||||
|
||||
from .app import create_app
|
||||
|
||||
|
@ -17,7 +17,6 @@ from loguru import logger
|
||||
import lnbits.settings
|
||||
from lnbits.core.tasks import register_task_listeners
|
||||
|
||||
from .commands import db_migrate, handle_assets
|
||||
from .core import core_app
|
||||
from .core.views.generic import core_html_routes
|
||||
from .helpers import (
|
||||
@ -45,10 +44,19 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
|
||||
"""
|
||||
configure_logger()
|
||||
|
||||
app = FastAPI()
|
||||
app.mount("/static", StaticFiles(directory="lnbits/static"), name="static")
|
||||
app = FastAPI(
|
||||
title="LNbits API",
|
||||
description="API for LNbits, the free and open source bitcoin wallet and accounts system with plugins.",
|
||||
license_info={
|
||||
"name": "MIT License",
|
||||
"url": "https://raw.githubusercontent.com/lnbits/lnbits-legend/main/LICENSE",
|
||||
},
|
||||
)
|
||||
app.mount("/static", StaticFiles(packages=[("lnbits", "static")]), name="static")
|
||||
app.mount(
|
||||
"/core/static", StaticFiles(directory="lnbits/core/static"), name="core_static"
|
||||
"/core/static",
|
||||
StaticFiles(packages=[("lnbits.core", "static")]),
|
||||
name="core_static",
|
||||
)
|
||||
|
||||
origins = ["*"]
|
||||
@ -84,7 +92,6 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
|
||||
check_funding_source(app)
|
||||
register_assets(app)
|
||||
register_routes(app)
|
||||
# register_commands(app)
|
||||
register_async_tasks(app)
|
||||
register_exception_handlers(app)
|
||||
|
||||
@ -137,12 +144,6 @@ def register_routes(app: FastAPI) -> None:
|
||||
)
|
||||
|
||||
|
||||
def register_commands(app: FastAPI):
|
||||
"""Register Click commands."""
|
||||
app.cli.add_command(db_migrate)
|
||||
app.cli.add_command(handle_assets)
|
||||
|
||||
|
||||
def register_assets(app: FastAPI):
|
||||
"""Serve each vendored asset separately or a bundle."""
|
||||
|
||||
|
@ -113,7 +113,7 @@ async def create_wallet(
|
||||
async def update_wallet(
|
||||
wallet_id: str, new_name: str, conn: Optional[Connection] = None
|
||||
) -> Optional[Wallet]:
|
||||
await (conn or db).execute(
|
||||
return await (conn or db).execute(
|
||||
"""
|
||||
UPDATE wallets SET
|
||||
name = ?
|
||||
|
@ -106,6 +106,8 @@ class Payment(BaseModel):
|
||||
|
||||
@property
|
||||
def tag(self) -> Optional[str]:
|
||||
if self.extra is None:
|
||||
return ""
|
||||
return self.extra.get("tag")
|
||||
|
||||
@property
|
||||
|
@ -109,18 +109,15 @@ async def pay_invoice(
|
||||
raise ValueError("Amount in invoice is too high.")
|
||||
|
||||
# put all parameters that don't change here
|
||||
PaymentKwargs = TypedDict(
|
||||
"PaymentKwargs",
|
||||
{
|
||||
"wallet_id": str,
|
||||
"payment_request": str,
|
||||
"payment_hash": str,
|
||||
"amount": int,
|
||||
"memo": str,
|
||||
"extra": Optional[Dict],
|
||||
},
|
||||
)
|
||||
payment_kwargs: PaymentKwargs = dict(
|
||||
class PaymentKwargs(TypedDict):
|
||||
wallet_id: str
|
||||
payment_request: str
|
||||
payment_hash: str
|
||||
amount: int
|
||||
memo: str
|
||||
extra: Optional[Dict]
|
||||
|
||||
payment_kwargs: PaymentKwargs = PaymentKwargs(
|
||||
wallet_id=wallet_id,
|
||||
payment_request=payment_request,
|
||||
payment_hash=invoice.payment_hash,
|
||||
@ -272,6 +269,7 @@ async def perform_lnurlauth(
|
||||
cb = urlparse(callback)
|
||||
|
||||
k1 = unhexlify(parse_qs(cb.query)["k1"][0])
|
||||
|
||||
key = wallet.wallet.lnurlauth_key(cb.netloc)
|
||||
|
||||
def int_to_bytes_suitable_der(x: int) -> bytes:
|
||||
|
@ -55,7 +55,7 @@ async def dispatch_webhook(payment: Payment):
|
||||
data = payment.dict()
|
||||
try:
|
||||
logger.debug("sending webhook", payment.webhook)
|
||||
r = await client.post(payment.webhook, json=data, timeout=40)
|
||||
r = await client.post(payment.webhook, json=data, timeout=40) # type: ignore
|
||||
await mark_webhook_sent(payment, r.status_code)
|
||||
except (httpx.ConnectError, httpx.RequestError):
|
||||
await mark_webhook_sent(payment, -1)
|
||||
|
@ -3,7 +3,7 @@ import hashlib
|
||||
import json
|
||||
from binascii import unhexlify
|
||||
from http import HTTPStatus
|
||||
from typing import Dict, List, Optional, Union
|
||||
from typing import Dict, List, Optional, Tuple, Union
|
||||
from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse
|
||||
|
||||
import httpx
|
||||
@ -185,7 +185,7 @@ async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet):
|
||||
assert (
|
||||
data.lnurl_balance_check is not None
|
||||
), "lnurl_balance_check is required"
|
||||
save_balance_check(wallet.id, data.lnurl_balance_check)
|
||||
await save_balance_check(wallet.id, data.lnurl_balance_check)
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
try:
|
||||
@ -248,7 +248,7 @@ async def api_payments_pay_invoice(bolt11: str, wallet: Wallet):
|
||||
)
|
||||
async def api_payments_create(
|
||||
wallet: WalletTypeInfo = Depends(require_invoice_key),
|
||||
invoiceData: CreateInvoiceData = Body(...),
|
||||
invoiceData: CreateInvoiceData = Body(...), # type: ignore
|
||||
):
|
||||
if invoiceData.out is True and wallet.wallet_type == 0:
|
||||
if not invoiceData.bolt11:
|
||||
@ -291,7 +291,7 @@ async def api_payments_pay_lnurl(
|
||||
timeout=40,
|
||||
)
|
||||
if r.is_error:
|
||||
raise httpx.ConnectError
|
||||
raise httpx.ConnectError("LNURL callback connection error")
|
||||
except (httpx.ConnectError, httpx.RequestError):
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.BAD_REQUEST,
|
||||
@ -354,7 +354,7 @@ async def subscribe(request: Request, wallet: Wallet):
|
||||
logger.debug("adding sse listener", payment_queue)
|
||||
api_invoice_listeners.append(payment_queue)
|
||||
|
||||
send_queue: asyncio.Queue[tuple[str, Payment]] = asyncio.Queue(0)
|
||||
send_queue: asyncio.Queue[Tuple[str, Payment]] = asyncio.Queue(0)
|
||||
|
||||
async def payment_received() -> None:
|
||||
while True:
|
||||
@ -393,16 +393,13 @@ async def api_payments_sse(
|
||||
async def api_payment(payment_hash, X_Api_Key: Optional[str] = Header(None)):
|
||||
# We use X_Api_Key here because we want this call to work with and without keys
|
||||
# If a valid key is given, we also return the field "details", otherwise not
|
||||
wallet = None
|
||||
try:
|
||||
if X_Api_Key.extra:
|
||||
logger.warning("No key")
|
||||
except:
|
||||
wallet = await get_wallet_for_key(X_Api_Key)
|
||||
wallet = await get_wallet_for_key(X_Api_Key) if type(X_Api_Key) == str else None
|
||||
|
||||
# we have to specify the wallet id here, because postgres and sqlite return internal payments in different order
|
||||
# and get_standalone_payment otherwise just fetches the first one, causing unpredictable results
|
||||
payment = await get_standalone_payment(
|
||||
payment_hash, wallet_id=wallet.id if wallet else None
|
||||
) # we have to specify the wallet id here, because postgres and sqlite return internal payments in different order
|
||||
# and get_standalone_payment otherwise just fetches the first one, causing unpredictable results
|
||||
)
|
||||
if payment is None:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.NOT_FOUND, detail="Payment does not exist."
|
||||
@ -488,7 +485,8 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type
|
||||
)
|
||||
|
||||
try:
|
||||
tag = data["tag"]
|
||||
tag: str = data.get("tag")
|
||||
params.update(**data)
|
||||
if tag == "channelRequest":
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.BAD_REQUEST,
|
||||
@ -498,10 +496,7 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type
|
||||
"message": "unsupported",
|
||||
},
|
||||
)
|
||||
|
||||
params.update(**data)
|
||||
|
||||
if tag == "withdrawRequest":
|
||||
elif tag == "withdrawRequest":
|
||||
params.update(kind="withdraw")
|
||||
params.update(fixed=data["minWithdrawable"] == data["maxWithdrawable"])
|
||||
|
||||
@ -519,8 +514,7 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type
|
||||
query=urlencode(qs, doseq=True)
|
||||
)
|
||||
params.update(callback=urlunparse(parsed_callback))
|
||||
|
||||
if tag == "payRequest":
|
||||
elif tag == "payRequest":
|
||||
params.update(kind="pay")
|
||||
params.update(fixed=data["minSendable"] == data["maxSendable"])
|
||||
|
||||
@ -538,8 +532,8 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type
|
||||
params.update(image=data_uri)
|
||||
if k == "text/email" or k == "text/identifier":
|
||||
params.update(targetUser=v)
|
||||
|
||||
params.update(commentAllowed=data.get("commentAllowed", 0))
|
||||
|
||||
except KeyError as exc:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.SERVICE_UNAVAILABLE,
|
||||
@ -612,8 +606,8 @@ class ConversionData(BaseModel):
|
||||
async def api_fiat_as_sats(data: ConversionData):
|
||||
output = {}
|
||||
if data.from_ == "sat":
|
||||
output["sats"] = int(data.amount)
|
||||
output["BTC"] = data.amount / 100000000
|
||||
output["sats"] = int(data.amount)
|
||||
for currency in data.to.split(","):
|
||||
output[currency.strip().upper()] = await satoshis_amount_as_fiat(
|
||||
data.amount, currency.strip()
|
||||
|
@ -55,9 +55,9 @@ async def home(request: Request, lightning: str = None):
|
||||
)
|
||||
async def extensions(
|
||||
request: Request,
|
||||
user: User = Depends(check_user_exists),
|
||||
enable: str = Query(None),
|
||||
disable: str = Query(None),
|
||||
user: User = Depends(check_user_exists), # type: ignore
|
||||
enable: str = Query(None), # type: ignore
|
||||
disable: str = Query(None), # type: ignore
|
||||
):
|
||||
extension_to_enable = enable
|
||||
extension_to_disable = disable
|
||||
@ -88,7 +88,7 @@ async def extensions(
|
||||
|
||||
# Update user as his extensions have been updated
|
||||
if extension_to_enable or extension_to_disable:
|
||||
user = await get_user(user.id)
|
||||
user = await get_user(user.id) # type: ignore
|
||||
|
||||
return template_renderer().TemplateResponse(
|
||||
"core/extensions.html", {"request": request, "user": user.dict()}
|
||||
@ -109,10 +109,10 @@ nothing: create everything<br>
|
||||
""",
|
||||
)
|
||||
async def wallet(
|
||||
request: Request = Query(None),
|
||||
nme: Optional[str] = Query(None),
|
||||
usr: Optional[UUID4] = Query(None),
|
||||
wal: Optional[UUID4] = Query(None),
|
||||
request: Request = Query(None), # type: ignore
|
||||
nme: Optional[str] = Query(None), # type: ignore
|
||||
usr: Optional[UUID4] = Query(None), # type: ignore
|
||||
wal: Optional[UUID4] = Query(None), # type: ignore
|
||||
):
|
||||
user_id = usr.hex if usr else None
|
||||
wallet_id = wal.hex if wal else None
|
||||
@ -121,7 +121,7 @@ async def wallet(
|
||||
|
||||
if not user_id:
|
||||
user = await get_user((await create_account()).id)
|
||||
logger.info(f"Create user {user.id}")
|
||||
logger.info(f"Create user {user.id}") # type: ignore
|
||||
else:
|
||||
user = await get_user(user_id)
|
||||
if not user:
|
||||
@ -135,22 +135,22 @@ async def wallet(
|
||||
if LNBITS_ADMIN_USERS and user_id in LNBITS_ADMIN_USERS:
|
||||
user.admin = True
|
||||
if not wallet_id:
|
||||
if user.wallets and not wallet_name:
|
||||
wallet = user.wallets[0]
|
||||
if user.wallets and not wallet_name: # type: ignore
|
||||
wallet = user.wallets[0] # type: ignore
|
||||
else:
|
||||
wallet = await create_wallet(user_id=user.id, wallet_name=wallet_name)
|
||||
wallet = await create_wallet(user_id=user.id, wallet_name=wallet_name) # type: ignore
|
||||
logger.info(
|
||||
f"Created new wallet {wallet_name if wallet_name else '(no name)'} for user {user.id}"
|
||||
f"Created new wallet {wallet_name if wallet_name else '(no name)'} for user {user.id}" # type: ignore
|
||||
)
|
||||
|
||||
return RedirectResponse(
|
||||
f"/wallet?usr={user.id}&wal={wallet.id}",
|
||||
f"/wallet?usr={user.id}&wal={wallet.id}", # type: ignore
|
||||
status_code=status.HTTP_307_TEMPORARY_REDIRECT,
|
||||
)
|
||||
|
||||
logger.debug(f"Access wallet {wallet_name}{'of user '+ user.id if user else ''}")
|
||||
wallet = user.get_wallet(wallet_id)
|
||||
if not wallet:
|
||||
userwallet = user.get_wallet(wallet_id) # type: ignore
|
||||
if not userwallet:
|
||||
return template_renderer().TemplateResponse(
|
||||
"error.html", {"request": request, "err": "Wallet not found"}
|
||||
)
|
||||
@ -159,10 +159,10 @@ async def wallet(
|
||||
"core/wallet.html",
|
||||
{
|
||||
"request": request,
|
||||
"user": user.dict(),
|
||||
"wallet": wallet.dict(),
|
||||
"user": user.dict(), # type: ignore
|
||||
"wallet": userwallet.dict(),
|
||||
"service_fee": service_fee,
|
||||
"web_manifest": f"/manifest/{user.id}.webmanifest",
|
||||
"web_manifest": f"/manifest/{user.id}.webmanifest", # type: ignore
|
||||
},
|
||||
)
|
||||
|
||||
@ -216,20 +216,20 @@ async def lnurl_full_withdraw_callback(request: Request):
|
||||
|
||||
|
||||
@core_html_routes.get("/deletewallet", response_class=RedirectResponse)
|
||||
async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query(...)):
|
||||
async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query(...)): # type: ignore
|
||||
user = await get_user(usr)
|
||||
user_wallet_ids = [u.id for u in user.wallets]
|
||||
user_wallet_ids = [u.id for u in user.wallets] # type: ignore
|
||||
|
||||
if wal not in user_wallet_ids:
|
||||
raise HTTPException(HTTPStatus.FORBIDDEN, "Not your wallet.")
|
||||
else:
|
||||
await delete_wallet(user_id=user.id, wallet_id=wal)
|
||||
await delete_wallet(user_id=user.id, wallet_id=wal) # type: ignore
|
||||
user_wallet_ids.remove(wal)
|
||||
logger.debug("Deleted wallet {wal} of user {user.id}")
|
||||
|
||||
if user_wallet_ids:
|
||||
return RedirectResponse(
|
||||
url_for("/wallet", usr=user.id, wal=user_wallet_ids[0]),
|
||||
url_for("/wallet", usr=user.id, wal=user_wallet_ids[0]), # type: ignore
|
||||
status_code=status.HTTP_307_TEMPORARY_REDIRECT,
|
||||
)
|
||||
|
||||
@ -242,7 +242,7 @@ async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query
|
||||
async def lnurl_balance_notify(request: Request, service: str):
|
||||
bc = await get_balance_check(request.query_params.get("wal"), service)
|
||||
if bc:
|
||||
redeem_lnurl_withdraw(bc.wallet, bc.url)
|
||||
await redeem_lnurl_withdraw(bc.wallet, bc.url)
|
||||
|
||||
|
||||
@core_html_routes.get(
|
||||
@ -252,7 +252,7 @@ async def lnurlwallet(request: Request):
|
||||
async with db.connect() as conn:
|
||||
account = await create_account(conn=conn)
|
||||
user = await get_user(account.id, conn=conn)
|
||||
wallet = await create_wallet(user_id=user.id, conn=conn)
|
||||
wallet = await create_wallet(user_id=user.id, conn=conn) # type: ignore
|
||||
|
||||
asyncio.create_task(
|
||||
redeem_lnurl_withdraw(
|
||||
@ -265,7 +265,7 @@ async def lnurlwallet(request: Request):
|
||||
)
|
||||
|
||||
return RedirectResponse(
|
||||
f"/wallet?usr={user.id}&wal={wallet.id}",
|
||||
f"/wallet?usr={user.id}&wal={wallet.id}", # type: ignore
|
||||
status_code=status.HTTP_307_TEMPORARY_REDIRECT,
|
||||
)
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
from http import HTTPStatus
|
||||
from typing import Union
|
||||
|
||||
from cerberus import Validator # type: ignore
|
||||
from fastapi import status
|
||||
@ -29,20 +30,21 @@ class KeyChecker(SecurityBase):
|
||||
self._key_type = "invoice"
|
||||
self._api_key = api_key
|
||||
if api_key:
|
||||
self.model: APIKey = APIKey(
|
||||
key = APIKey(
|
||||
**{"in": APIKeyIn.query},
|
||||
name="X-API-KEY",
|
||||
description="Wallet API Key - QUERY",
|
||||
)
|
||||
else:
|
||||
self.model: APIKey = APIKey(
|
||||
key = APIKey(
|
||||
**{"in": APIKeyIn.header},
|
||||
name="X-API-KEY",
|
||||
description="Wallet API Key - HEADER",
|
||||
)
|
||||
self.wallet = None
|
||||
self.wallet = None # type: ignore
|
||||
self.model: APIKey = key
|
||||
|
||||
async def __call__(self, request: Request) -> Wallet:
|
||||
async def __call__(self, request: Request):
|
||||
try:
|
||||
key_value = (
|
||||
self._api_key
|
||||
@ -52,7 +54,7 @@ class KeyChecker(SecurityBase):
|
||||
# FIXME: Find another way to validate the key. A fetch from DB should be avoided here.
|
||||
# Also, we should not return the wallet here - thats silly.
|
||||
# Possibly store it in a Redis DB
|
||||
self.wallet = await get_wallet_for_key(key_value, self._key_type)
|
||||
self.wallet = await get_wallet_for_key(key_value, self._key_type) # type: ignore
|
||||
if not self.wallet:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.UNAUTHORIZED,
|
||||
@ -120,8 +122,8 @@ api_key_query = APIKeyQuery(
|
||||
|
||||
async def get_key_type(
|
||||
r: Request,
|
||||
api_key_header: str = Security(api_key_header),
|
||||
api_key_query: str = Security(api_key_query),
|
||||
api_key_header: str = Security(api_key_header), # type: ignore
|
||||
api_key_query: str = Security(api_key_query), # type: ignore
|
||||
) -> WalletTypeInfo:
|
||||
# 0: admin
|
||||
# 1: invoice
|
||||
@ -134,9 +136,9 @@ async def get_key_type(
|
||||
token = api_key_header if api_key_header else api_key_query
|
||||
|
||||
try:
|
||||
checker = WalletAdminKeyChecker(api_key=token)
|
||||
await checker.__call__(r)
|
||||
wallet = WalletTypeInfo(0, checker.wallet)
|
||||
admin_checker = WalletAdminKeyChecker(api_key=token)
|
||||
await admin_checker.__call__(r)
|
||||
wallet = WalletTypeInfo(0, admin_checker.wallet) # type: ignore
|
||||
if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and (
|
||||
LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS
|
||||
):
|
||||
@ -153,9 +155,9 @@ async def get_key_type(
|
||||
raise
|
||||
|
||||
try:
|
||||
checker = WalletInvoiceKeyChecker(api_key=token)
|
||||
await checker.__call__(r)
|
||||
wallet = WalletTypeInfo(1, checker.wallet)
|
||||
invoice_checker = WalletInvoiceKeyChecker(api_key=token)
|
||||
await invoice_checker.__call__(r)
|
||||
wallet = WalletTypeInfo(1, invoice_checker.wallet) # type: ignore
|
||||
if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and (
|
||||
LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS
|
||||
):
|
||||
@ -167,15 +169,16 @@ async def get_key_type(
|
||||
if e.status_code == HTTPStatus.BAD_REQUEST:
|
||||
raise
|
||||
if e.status_code == HTTPStatus.UNAUTHORIZED:
|
||||
return WalletTypeInfo(2, None)
|
||||
return WalletTypeInfo(2, None) # type: ignore
|
||||
except:
|
||||
raise
|
||||
return wallet
|
||||
|
||||
|
||||
async def require_admin_key(
|
||||
r: Request,
|
||||
api_key_header: str = Security(api_key_header),
|
||||
api_key_query: str = Security(api_key_query),
|
||||
api_key_header: str = Security(api_key_header), # type: ignore
|
||||
api_key_query: str = Security(api_key_query), # type: ignore
|
||||
):
|
||||
token = api_key_header if api_key_header else api_key_query
|
||||
|
||||
@ -193,8 +196,8 @@ async def require_admin_key(
|
||||
|
||||
async def require_invoice_key(
|
||||
r: Request,
|
||||
api_key_header: str = Security(api_key_header),
|
||||
api_key_query: str = Security(api_key_query),
|
||||
api_key_header: str = Security(api_key_header), # type: ignore
|
||||
api_key_query: str = Security(api_key_query), # type: ignore
|
||||
):
|
||||
token = api_key_header if api_key_header else api_key_query
|
||||
|
||||
|
@ -9,7 +9,7 @@ db = Database("ext_bleskomat")
|
||||
bleskomat_static_files = [
|
||||
{
|
||||
"path": "/bleskomat/static",
|
||||
"app": StaticFiles(directory="lnbits/extensions/bleskomat/static"),
|
||||
"app": StaticFiles(packages=[("lnbits", "extensions/bleskomat/static")]),
|
||||
"name": "bleskomat_static",
|
||||
}
|
||||
]
|
||||
|
@ -62,4 +62,5 @@
|
||||
</p>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
<q-btn flat label="Swagger API" type="a" href="../docs#/Bleskomat"></q-btn>
|
||||
</q-expansion-item>
|
||||
|
@ -12,7 +12,7 @@ db = Database("ext_copilot")
|
||||
copilot_static_files = [
|
||||
{
|
||||
"path": "/copilot/static",
|
||||
"app": StaticFiles(directory="lnbits/extensions/copilot/static"),
|
||||
"app": StaticFiles(packages=[("lnbits", "extensions/copilot/static")]),
|
||||
"name": "copilot_static",
|
||||
}
|
||||
]
|
||||
|
@ -14,6 +14,7 @@
|
||||
label="API info"
|
||||
:content-inset-level="0.5"
|
||||
>
|
||||
<q-btn flat label="Swagger API" type="a" href="../docs#/copilot"></q-btn>
|
||||
<q-expansion-item group="api" dense expand-separator label="Create copilot">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
|
@ -9,7 +9,7 @@ db = Database("ext_discordbot")
|
||||
discordbot_static_files = [
|
||||
{
|
||||
"path": "/discordbot/static",
|
||||
"app": StaticFiles(directory="lnbits/extensions/discordbot/static"),
|
||||
"app": StaticFiles(packages=[("lnbits", "extensions/discordbot/static")]),
|
||||
"name": "discordbot_static",
|
||||
}
|
||||
]
|
||||
|
@ -34,6 +34,7 @@
|
||||
label="API info"
|
||||
:content-inset-level="0.5"
|
||||
>
|
||||
<q-btn flat label="Swagger API" type="a" href="../docs#/discordbot"></q-btn>
|
||||
<q-expansion-item group="api" dense expand-separator label="GET users">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
|
@ -16,7 +16,7 @@ async def create_ticket(
|
||||
INSERT INTO events.ticket (id, wallet, event, name, email, registered, paid)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(payment_hash, wallet, event, name, email, False, False),
|
||||
(payment_hash, wallet, event, name, email, False, True),
|
||||
)
|
||||
|
||||
ticket = await get_ticket(payment_hash)
|
||||
|
@ -20,4 +20,5 @@
|
||||
</p>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
<q-btn flat label="Swagger API" type="a" href="../docs#/events"></q-btn>
|
||||
</q-expansion-item>
|
||||
|
@ -135,15 +135,7 @@
|
||||
var self = this
|
||||
axios
|
||||
|
||||
.post(
|
||||
'/events/api/v1/tickets/' + '{{ event_id }}/{{ event_price }}',
|
||||
{
|
||||
event: '{{ event_id }}',
|
||||
event_name: '{{ event_name }}',
|
||||
name: self.formDialog.data.name,
|
||||
email: self.formDialog.data.email
|
||||
}
|
||||
)
|
||||
.get('/events/api/v1/tickets/' + '{{ event_id }}')
|
||||
.then(function (response) {
|
||||
self.paymentReq = response.data.payment_request
|
||||
self.paymentCheck = response.data.payment_hash
|
||||
@ -161,7 +153,17 @@
|
||||
|
||||
paymentChecker = setInterval(function () {
|
||||
axios
|
||||
.get('/events/api/v1/tickets/' + self.paymentCheck)
|
||||
.post(
|
||||
'/events/api/v1/tickets/' +
|
||||
'{{ event_id }}/' +
|
||||
self.paymentCheck,
|
||||
{
|
||||
event: '{{ event_id }}',
|
||||
event_name: '{{ event_name }}',
|
||||
name: self.formDialog.data.name,
|
||||
email: self.formDialog.data.email
|
||||
}
|
||||
)
|
||||
.then(function (res) {
|
||||
if (res.data.paid) {
|
||||
clearInterval(paymentChecker)
|
||||
|
@ -133,7 +133,10 @@
|
||||
var self = this
|
||||
|
||||
LNbits.api
|
||||
.request('GET', '/events/api/v1/register/ticket/' + res)
|
||||
.request(
|
||||
'GET',
|
||||
'/events/api/v1/register/ticket/' + res.split('//')[1]
|
||||
)
|
||||
.then(function (response) {
|
||||
self.$q.notify({
|
||||
type: 'positive',
|
||||
|
@ -13,9 +13,8 @@
|
||||
<br />
|
||||
|
||||
<qrcode
|
||||
:value="'{{ ticket_id }}'"
|
||||
:options="{width: 340}"
|
||||
class="rounded-borders"
|
||||
:value="'ticket://{{ ticket_id }}'"
|
||||
:options="{width: 500}"
|
||||
></qrcode>
|
||||
<br />
|
||||
<q-btn @click="printWindow" color="grey" class="q-ml-auto">
|
||||
|
@ -97,8 +97,8 @@ async def api_tickets(
|
||||
return [ticket.dict() for ticket in await get_tickets(wallet_ids)]
|
||||
|
||||
|
||||
@events_ext.post("/api/v1/tickets/{event_id}/{sats}")
|
||||
async def api_ticket_make_ticket(event_id, sats, data: CreateTicket):
|
||||
@events_ext.get("/api/v1/tickets/{event_id}")
|
||||
async def api_ticket_make_ticket(event_id):
|
||||
event = await get_event(event_id)
|
||||
if not event:
|
||||
raise HTTPException(
|
||||
@ -107,13 +107,22 @@ async def api_ticket_make_ticket(event_id, sats, data: CreateTicket):
|
||||
try:
|
||||
payment_hash, payment_request = await create_invoice(
|
||||
wallet_id=event.wallet,
|
||||
amount=int(sats),
|
||||
amount=event.price_per_ticket,
|
||||
memo=f"{event_id}",
|
||||
extra={"tag": "events"},
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
|
||||
|
||||
return {"payment_hash": payment_hash, "payment_request": payment_request}
|
||||
|
||||
|
||||
@events_ext.post("/api/v1/tickets/{event_id}/{payment_hash}")
|
||||
async def api_ticket_send_ticket(event_id, payment_hash, data: CreateTicket):
|
||||
event = await get_event(event_id)
|
||||
try:
|
||||
status = await api_payment(payment_hash)
|
||||
if status["paid"]:
|
||||
ticket = await create_ticket(
|
||||
payment_hash=payment_hash,
|
||||
wallet=event.wallet,
|
||||
@ -124,20 +133,10 @@ async def api_ticket_make_ticket(event_id, sats, data: CreateTicket):
|
||||
|
||||
if not ticket:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.NOT_FOUND, detail=f"Event could not be fetched."
|
||||
status_code=HTTPStatus.NOT_FOUND,
|
||||
detail=f"Event could not be fetched.",
|
||||
)
|
||||
|
||||
return {"payment_hash": payment_hash, "payment_request": payment_request}
|
||||
|
||||
|
||||
@events_ext.get("/api/v1/tickets/{payment_hash}")
|
||||
async def api_ticket_send_ticket(payment_hash):
|
||||
ticket = await get_ticket(payment_hash)
|
||||
|
||||
try:
|
||||
status = await api_payment(payment_hash)
|
||||
if status["paid"]:
|
||||
await set_ticket_paid(payment_hash=payment_hash)
|
||||
return {"paid": True, "ticket_id": ticket.id}
|
||||
except Exception:
|
||||
raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail="Not paid")
|
||||
|
@ -12,7 +12,7 @@ db = Database("ext_jukebox")
|
||||
jukebox_static_files = [
|
||||
{
|
||||
"path": "/jukebox/static",
|
||||
"app": StaticFiles(directory="lnbits/extensions/jukebox/static"),
|
||||
"app": StaticFiles(packages=[("lnbits", "extensions/jukebox/static")]),
|
||||
"name": "jukebox_static",
|
||||
}
|
||||
]
|
||||
|
@ -24,6 +24,8 @@
|
||||
label="API info"
|
||||
:content-inset-level="0.5"
|
||||
>
|
||||
<q-btn flat label="Swagger API" type="a" href="../docs#/jukebox"></q-btn>
|
||||
|
||||
<q-expansion-item group="api" dense expand-separator label="List jukeboxes">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
|
@ -12,7 +12,7 @@ db = Database("ext_livestream")
|
||||
livestream_static_files = [
|
||||
{
|
||||
"path": "/livestream/static",
|
||||
"app": StaticFiles(directory="lnbits/extensions/livestream/static"),
|
||||
"app": StaticFiles(packages=[("lnbits", "extensions/livestream/static")]),
|
||||
"name": "livestream_static",
|
||||
}
|
||||
]
|
||||
|
@ -17,6 +17,8 @@
|
||||
label="API info"
|
||||
:content-inset-level="0.5"
|
||||
>
|
||||
<q-btn flat label="Swagger API" type="a" href="../docs#/livestream"></q-btn>
|
||||
|
||||
<q-expansion-item
|
||||
group="api"
|
||||
dense
|
||||
|
@ -31,6 +31,7 @@
|
||||
label="API info"
|
||||
:content-inset-level="0.5"
|
||||
>
|
||||
<q-btn flat label="Swagger API" type="a" href="../docs#/lnaddress"></q-btn>
|
||||
<q-expansion-item group="api" dense expand-separator label="GET domains">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
|
@ -31,5 +31,6 @@
|
||||
</li>
|
||||
</ul>
|
||||
</q-card-section>
|
||||
<q-btn flat label="Swagger API" type="a" href="../docs#/lndhub"></q-btn>
|
||||
</q-card>
|
||||
</q-expansion-item>
|
||||
|
@ -19,4 +19,5 @@
|
||||
</p>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
<q-btn flat label="Swagger API" type="a" href="../docs#/lnticket"></q-btn>
|
||||
</q-expansion-item>
|
||||
|
@ -17,6 +17,12 @@
|
||||
label="API info"
|
||||
:content-inset-level="0.5"
|
||||
>
|
||||
<q-btn
|
||||
flat
|
||||
label="Swagger API"
|
||||
type="a"
|
||||
href="../docs#/lnurldevice"
|
||||
></q-btn>
|
||||
<q-expansion-item
|
||||
group="api"
|
||||
dense
|
||||
|
@ -12,7 +12,7 @@ db = Database("ext_lnurlp")
|
||||
lnurlp_static_files = [
|
||||
{
|
||||
"path": "/lnurlp/static",
|
||||
"app": StaticFiles(directory="lnbits/extensions/lnurlp/static"),
|
||||
"app": StaticFiles(packages=[("lnbits", "extensions/lnurlp/static")]),
|
||||
"name": "lnurlp_static",
|
||||
}
|
||||
]
|
||||
|
@ -4,6 +4,7 @@
|
||||
label="API info"
|
||||
:content-inset-level="0.5"
|
||||
>
|
||||
<q-btn flat label="Swagger API" type="a" href="../docs#/lnurlp"></q-btn>
|
||||
<q-expansion-item group="api" dense expand-separator label="List pay links">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
@ -51,6 +52,7 @@
|
||||
expand-separator
|
||||
label="Create a pay link"
|
||||
>
|
||||
<q-btn flat label="Swagger API" type="a" href="../docs#/lnurlp"></q-btn>
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<code><span class="text-green">POST</span> /lnurlp/api/v1/links</code>
|
||||
|
@ -2,5 +2,5 @@
|
||||
"name": "LNURLPayout",
|
||||
"short_description": "Autodump wallet funds to LNURLpay",
|
||||
"icon": "exit_to_app",
|
||||
"contributors": ["arcbtc"]
|
||||
"contributors": ["arcbtc","talvasconcelos"]
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
label="API info"
|
||||
:content-inset-level="0.5"
|
||||
>
|
||||
<q-btn flat label="Swagger API" type="a" href="../docs#/lnurlpayout"></q-btn>
|
||||
<q-expansion-item group="api" dense expand-separator label="List lnurlpayout">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
|
@ -9,7 +9,7 @@ db = Database("ext_offlineshop")
|
||||
offlineshop_static_files = [
|
||||
{
|
||||
"path": "/offlineshop/static",
|
||||
"app": StaticFiles(directory="lnbits/extensions/offlineshop/static"),
|
||||
"app": StaticFiles(packages=[("lnbits", "extensions/offlineshop/static")]),
|
||||
"name": "offlineshop_static",
|
||||
}
|
||||
]
|
||||
|
@ -47,6 +47,7 @@
|
||||
label="API info"
|
||||
:content-inset-level="0.5"
|
||||
>
|
||||
<q-btn flat label="Swagger API" type="a" href="../docs#/offlineshop"></q-btn>
|
||||
<q-expansion-item
|
||||
group="api"
|
||||
dense
|
||||
|
@ -4,6 +4,7 @@
|
||||
label="API info"
|
||||
:content-inset-level="0.5"
|
||||
>
|
||||
<q-btn flat label="Swagger API" type="a" href="../docs#/paywall"></q-btn>
|
||||
<q-expansion-item group="api" dense expand-separator label="List paywalls">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
|
@ -4,6 +4,7 @@
|
||||
label="API info"
|
||||
:content-inset-level="0.5"
|
||||
>
|
||||
<q-btn flat label="Swagger API" type="a" href="../docs#/satsdice"></q-btn>
|
||||
<q-expansion-item group="api" dense expand-separator label="List satsdices">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
|
@ -15,6 +15,7 @@
|
||||
label="API info"
|
||||
:content-inset-level="0.5"
|
||||
>
|
||||
<q-btn flat label="Swagger API" type="a" href="../docs#/satspay"></q-btn>
|
||||
<q-expansion-item group="api" dense expand-separator label="Create charge">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
|
@ -12,7 +12,7 @@ db = Database("ext_splitpayments")
|
||||
splitpayments_static_files = [
|
||||
{
|
||||
"path": "/splitpayments/static",
|
||||
"app": StaticFiles(directory="lnbits/extensions/splitpayments/static"),
|
||||
"app": StaticFiles(packages=[("lnbits", "extensions/splitpayments/static")]),
|
||||
"name": "splitpayments_static",
|
||||
}
|
||||
]
|
||||
|
@ -28,6 +28,12 @@
|
||||
label="API info"
|
||||
:content-inset-level="0.5"
|
||||
>
|
||||
<q-btn
|
||||
flat
|
||||
label="Swagger API"
|
||||
type="a"
|
||||
href="../docs#/splitpayments"
|
||||
></q-btn>
|
||||
<q-expansion-item
|
||||
group="api"
|
||||
dense
|
||||
|
@ -15,4 +15,5 @@
|
||||
>
|
||||
</p>
|
||||
</q-card-section>
|
||||
<q-btn flat label="Swagger API" type="a" href="../docs#/streamalerts"></q-btn>
|
||||
</q-card>
|
||||
|
@ -22,5 +22,6 @@
|
||||
>
|
||||
</p>
|
||||
</q-card-section>
|
||||
<q-btn flat label="Swagger API" type="a" href="../docs#/subdomains"></q-btn>
|
||||
</q-card>
|
||||
</q-expansion-item>
|
||||
|
@ -12,4 +12,5 @@
|
||||
>
|
||||
</p>
|
||||
</q-card-section>
|
||||
<q-btn flat label="Swagger API" type="a" href="../docs#/tipjar"></q-btn>
|
||||
</q-card>
|
||||
|
@ -4,6 +4,7 @@
|
||||
label="API info"
|
||||
:content-inset-level="0.5"
|
||||
>
|
||||
<q-btn flat label="Swagger API" type="a" href="../docs#/tpos"></q-btn>
|
||||
<q-expansion-item group="api" dense expand-separator label="List TPoS">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
|
@ -308,7 +308,9 @@
|
||||
},
|
||||
sat: function () {
|
||||
if (!this.exchangeRate) return 0
|
||||
return Math.ceil((this.amount / this.exchangeRate) * 100000000)
|
||||
return Math.ceil(
|
||||
((this.amount - this.tipAmount) / this.exchangeRate) * 100000000
|
||||
)
|
||||
},
|
||||
tipAmountSat: function () {
|
||||
if (!this.exchangeRate) return 0
|
||||
@ -423,10 +425,9 @@
|
||||
'{{ tpos.tip_options | tojson }}' == 'null'
|
||||
? null
|
||||
: JSON.parse('{{ tpos.tip_options }}')
|
||||
console.log(typeof this.tip_options, this.tip_options)
|
||||
setInterval(function () {
|
||||
getRates()
|
||||
}, 20000)
|
||||
}, 120000)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
@ -28,6 +28,7 @@
|
||||
label="API info"
|
||||
:content-inset-level="0.5"
|
||||
>
|
||||
<q-btn flat label="Swagger API" type="a" href="../docs#/usermanager"></q-btn>
|
||||
<q-expansion-item group="api" dense expand-separator label="GET users">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
|
@ -20,6 +20,7 @@
|
||||
label="API info"
|
||||
:content-inset-level="0.5"
|
||||
>
|
||||
<q-btn flat label="Swagger API" type="a" href="../docs#/watchonly"></q-btn>
|
||||
<q-expansion-item group="api" dense expand-separator label="List wallets">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
|
@ -9,7 +9,7 @@ db = Database("ext_withdraw")
|
||||
withdraw_static_files = [
|
||||
{
|
||||
"path": "/withdraw/static",
|
||||
"app": StaticFiles(directory="lnbits/extensions/withdraw/static"),
|
||||
"app": StaticFiles(packages=[("lnbits", "extensions/withdraw/static")]),
|
||||
"name": "withdraw_static",
|
||||
}
|
||||
]
|
||||
|
@ -4,6 +4,7 @@
|
||||
label="API info"
|
||||
:content-inset-level="0.5"
|
||||
>
|
||||
<q-btn flat label="Swagger API" type="a" href="../docs#/withdraw"></q-btn>
|
||||
<q-expansion-item
|
||||
group="api"
|
||||
dense
|
||||
|
@ -34,7 +34,7 @@ class ExtensionManager:
|
||||
|
||||
@property
|
||||
def extensions(self) -> List[Extension]:
|
||||
output = []
|
||||
output: List[Extension] = []
|
||||
|
||||
if "all" in self._disabled:
|
||||
return output
|
||||
|
18
lnbits/server.py
Normal file
18
lnbits/server.py
Normal file
@ -0,0 +1,18 @@
|
||||
import click
|
||||
import uvicorn
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option("--port", default="5000", help="Port to run LNBits on")
|
||||
@click.option("--host", default="127.0.0.1", help="Host to run LNBits on")
|
||||
def main(port, host):
|
||||
"""Launched with `poetry run lnbits` at root level"""
|
||||
uvicorn.run("lnbits.__main__:app", port=port, host=host)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
# def main():
|
||||
# """Launched with `poetry run start` at root level"""
|
||||
# uvicorn.run("lnbits.__main__:app")
|
@ -66,7 +66,7 @@ async def webhook_handler():
|
||||
raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
|
||||
|
||||
|
||||
internal_invoice_queue = asyncio.Queue(0)
|
||||
internal_invoice_queue: asyncio.Queue = asyncio.Queue(0)
|
||||
|
||||
|
||||
async def internal_invoice_listener():
|
||||
|
@ -1,5 +1,5 @@
|
||||
import asyncio
|
||||
from typing import Callable, NamedTuple
|
||||
from typing import Callable, List, NamedTuple
|
||||
|
||||
import httpx
|
||||
from loguru import logger
|
||||
@ -227,10 +227,10 @@ async def btc_price(currency: str) -> float:
|
||||
"TO": currency.upper(),
|
||||
"to": currency.lower(),
|
||||
}
|
||||
rates = []
|
||||
tasks = []
|
||||
rates: List[float] = []
|
||||
tasks: List[asyncio.Task] = []
|
||||
|
||||
send_channel = asyncio.Queue()
|
||||
send_channel: asyncio.Queue = asyncio.Queue()
|
||||
|
||||
async def controller():
|
||||
failures = 0
|
||||
|
@ -7,7 +7,10 @@ from typing import AsyncGenerator, Dict, Optional
|
||||
|
||||
import httpx
|
||||
from loguru import logger
|
||||
from websockets import connect
|
||||
|
||||
# TODO: https://github.com/lnbits/lnbits-legend/issues/764
|
||||
# mypy https://github.com/aaugustin/websockets/issues/940
|
||||
from websockets import connect # type: ignore
|
||||
from websockets.exceptions import (
|
||||
ConnectionClosed,
|
||||
ConnectionClosedError,
|
||||
|
@ -28,7 +28,7 @@ class FakeWallet(Wallet):
|
||||
logger.info(
|
||||
"FakeWallet funding source is for using LNbits as a centralised, stand-alone payment system with brrrrrr."
|
||||
)
|
||||
return StatusResponse(None, float("inf"))
|
||||
return StatusResponse(None, 1000000000)
|
||||
|
||||
async def create_invoice(
|
||||
self,
|
||||
@ -82,7 +82,7 @@ class FakeWallet(Wallet):
|
||||
invoice = decode(bolt11)
|
||||
if (
|
||||
hasattr(invoice, "checking_id")
|
||||
and invoice.checking_id[6:] == data["privkey"][:6]
|
||||
and invoice.checking_id[6:] == data["privkey"][:6] # type: ignore
|
||||
):
|
||||
return PaymentResponse(True, invoice.payment_hash, 0)
|
||||
else:
|
||||
@ -97,7 +97,7 @@ class FakeWallet(Wallet):
|
||||
return PaymentStatus(None)
|
||||
|
||||
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
|
||||
self.queue = asyncio.Queue(0)
|
||||
self.queue: asyncio.Queue = asyncio.Queue(0)
|
||||
while True:
|
||||
value = await self.queue.get()
|
||||
yield value
|
||||
|
@ -119,7 +119,7 @@ class LNPayWallet(Wallet):
|
||||
return PaymentStatus(statuses[r.json()["settled"]])
|
||||
|
||||
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
|
||||
self.queue = asyncio.Queue(0)
|
||||
self.queue: asyncio.Queue = asyncio.Queue(0)
|
||||
while True:
|
||||
value = await self.queue.get()
|
||||
yield value
|
||||
|
@ -73,10 +73,10 @@ class AESCipher(object):
|
||||
final_key += key
|
||||
return final_key[:output]
|
||||
|
||||
def decrypt(self, encrypted: str) -> str:
|
||||
def decrypt(self, encrypted: str) -> str: # type: ignore
|
||||
"""Decrypts a string using AES-256-CBC."""
|
||||
passphrase = self.passphrase
|
||||
encrypted = base64.b64decode(encrypted)
|
||||
encrypted = base64.b64decode(encrypted) # type: ignore
|
||||
assert encrypted[0:8] == b"Salted__"
|
||||
salt = encrypted[8:16]
|
||||
key_iv = self.bytes_to_key(passphrase.encode(), salt, 32 + 16)
|
||||
@ -84,7 +84,7 @@ class AESCipher(object):
|
||||
iv = key_iv[32:]
|
||||
aes = AES.new(key, AES.MODE_CBC, iv)
|
||||
try:
|
||||
return self.unpad(aes.decrypt(encrypted[16:])).decode()
|
||||
return self.unpad(aes.decrypt(encrypted[16:])).decode() # type: ignore
|
||||
except UnicodeDecodeError:
|
||||
raise ValueError("Wrong passphrase")
|
||||
|
||||
|
@ -127,7 +127,7 @@ class OpenNodeWallet(Wallet):
|
||||
return PaymentStatus(statuses[r.json()["data"]["status"]])
|
||||
|
||||
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
|
||||
self.queue = asyncio.Queue(0)
|
||||
self.queue: asyncio.Queue = asyncio.Queue(0)
|
||||
while True:
|
||||
value = await self.queue.get()
|
||||
yield value
|
||||
|
7
mypy.ini
7
mypy.ini
@ -1,7 +1,8 @@
|
||||
[mypy]
|
||||
ignore_missing_imports = True
|
||||
exclude = lnbits/wallets/lnd_grpc_files/
|
||||
exclude = lnbits/extensions/
|
||||
|
||||
exclude = (?x)(
|
||||
^lnbits/extensions.
|
||||
| ^lnbits/wallets/lnd_grpc_files.
|
||||
)
|
||||
[mypy-lnbits.wallets.lnd_grpc_files.*]
|
||||
follow_imports = skip
|
||||
|
106
nix/modules/lnbits-service.nix
Normal file
106
nix/modules/lnbits-service.nix
Normal file
@ -0,0 +1,106 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
let
|
||||
defaultUser = "lnbits";
|
||||
cfg = config.services.lnbits;
|
||||
inherit (lib) mkOption mkIf types optionalAttrs;
|
||||
in
|
||||
|
||||
{
|
||||
options = {
|
||||
services.lnbits = {
|
||||
enable = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Whether to enable the lnbits service
|
||||
'';
|
||||
};
|
||||
openFirewall = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to open the ports used by lnbits in the firewall for the server
|
||||
'';
|
||||
};
|
||||
package = mkOption {
|
||||
type = types.package;
|
||||
default = pkgs.lnbits;
|
||||
description = ''
|
||||
The lnbits package to use.
|
||||
'';
|
||||
};
|
||||
stateDir = mkOption {
|
||||
type = types.path;
|
||||
default = "/var/lib/lnbits";
|
||||
description = ''
|
||||
The lnbits state directory which LNBITS_DATA_FOLDER will be set to
|
||||
'';
|
||||
};
|
||||
host = mkOption {
|
||||
type = types.str;
|
||||
default = "127.0.0.1";
|
||||
description = ''
|
||||
The host to bind to
|
||||
'';
|
||||
};
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 8231;
|
||||
description = ''
|
||||
The port to run on
|
||||
'';
|
||||
};
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = "lnbits";
|
||||
description = "user to run lnbits as";
|
||||
};
|
||||
group = mkOption {
|
||||
type = types.str;
|
||||
default = "lnbits";
|
||||
description = "group to run lnbits as";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
users.users = optionalAttrs (cfg.user == defaultUser) {
|
||||
${defaultUser} = {
|
||||
isSystemUser = true;
|
||||
group = defaultUser;
|
||||
};
|
||||
};
|
||||
|
||||
users.groups = optionalAttrs (cfg.group == defaultUser) {
|
||||
${defaultUser} = { };
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d ${cfg.stateDir} 0700 ${cfg.user} ${cfg.group} - -"
|
||||
];
|
||||
|
||||
systemd.services.lnbits = {
|
||||
enable = true;
|
||||
description = "lnbits";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network-online.target" ];
|
||||
environment = {
|
||||
LNBITS_DATA_FOLDER = "${cfg.stateDir}";
|
||||
};
|
||||
serviceConfig = {
|
||||
User = cfg.user;
|
||||
Group = cfg.group;
|
||||
WorkingDirectory = "${cfg.package.src}";
|
||||
StateDirectory = "${cfg.stateDir}";
|
||||
ExecStart = "${lib.getExe cfg.package} --port ${toString cfg.port} --host ${cfg.host}";
|
||||
Restart = "always";
|
||||
PrivateTmp = true;
|
||||
};
|
||||
};
|
||||
networking.firewall = mkIf cfg.openFirewall {
|
||||
allowedTCPPorts = [ cfg.port ];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
4
nix/tests/default.nix
Normal file
4
nix/tests/default.nix
Normal file
@ -0,0 +1,4 @@
|
||||
{ pkgs, makeTest, inputs }:
|
||||
{
|
||||
vmTest = import ./nixos-module { inherit pkgs makeTest inputs; };
|
||||
}
|
25
nix/tests/nixos-module/default.nix
Normal file
25
nix/tests/nixos-module/default.nix
Normal file
@ -0,0 +1,25 @@
|
||||
{ pkgs, makeTest, inputs }:
|
||||
makeTest {
|
||||
nodes = {
|
||||
client = { config, pkgs, ... }: {
|
||||
environment.systemPackages = [ pkgs.curl ];
|
||||
};
|
||||
lnbits = { ... }: {
|
||||
imports = [ inputs.self.nixosModules.default ];
|
||||
services.lnbits = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
host = "0.0.0.0";
|
||||
};
|
||||
};
|
||||
};
|
||||
testScript = { nodes, ... }: ''
|
||||
start_all()
|
||||
lnbits.wait_for_open_port(${toString nodes.lnbits.config.services.lnbits.port})
|
||||
client.wait_for_unit("multi-user.target")
|
||||
with subtest("Check that the lnbits webserver can be reached."):
|
||||
assert "<title>LNbits</title>" in client.succeed(
|
||||
"curl -sSf http:/lnbits:8231/ | grep title"
|
||||
)
|
||||
'';
|
||||
}
|
1165
poetry.lock
generated
Normal file
1165
poetry.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
70
pyproject.toml
Normal file
70
pyproject.toml
Normal file
@ -0,0 +1,70 @@
|
||||
[tool.poetry]
|
||||
name = "lnbits"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
authors = ["matthewcroughan <matt@croughan.sh>"]
|
||||
|
||||
[tool.poetry.build]
|
||||
generate-setup-file = false
|
||||
script = "build.py"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.9"
|
||||
aiofiles = "0.7.0"
|
||||
asgiref = "3.4.1"
|
||||
attrs = "21.2.0"
|
||||
bech32 = "1.2.0"
|
||||
bitstring = "3.1.9"
|
||||
cerberus = "1.3.4"
|
||||
certifi = "2021.5.30"
|
||||
charset-normalizer = "2.0.6"
|
||||
click = "8.0.1"
|
||||
ecdsa = "0.17.0"
|
||||
embit = "0.4.9"
|
||||
environs = "9.3.3"
|
||||
fastapi = "0.78.0"
|
||||
h11 = "0.12.0"
|
||||
httpcore = "0.13.7"
|
||||
httptools = "0.2.0"
|
||||
httpx = "0.19.0"
|
||||
idna = "3.2"
|
||||
importlib-metadata = "4.8.1"
|
||||
jinja2 = "3.0.1"
|
||||
lnurl = "0.3.6"
|
||||
markupsafe = "2.0.1"
|
||||
marshmallow = "3.13.0"
|
||||
outcome = "1.1.0"
|
||||
psycopg2-binary = "2.9.1"
|
||||
pycryptodomex = "3.14.1"
|
||||
pydantic = "1.8.2"
|
||||
pypng = "0.0.21"
|
||||
pyqrcode = "1.2.1"
|
||||
pyscss = "1.3.7"
|
||||
python-dotenv = "0.19.0"
|
||||
pyyaml = "5.4.1"
|
||||
represent = "1.6.0.post0"
|
||||
rfc3986 = "1.5.0"
|
||||
secp256k1 = "0.14.0"
|
||||
shortuuid = "1.0.1"
|
||||
six = "1.16.0"
|
||||
sniffio = "1.2.0"
|
||||
sqlalchemy = "1.3.23"
|
||||
sqlalchemy-aio = "0.16.0"
|
||||
sse-starlette = "0.6.2"
|
||||
typing-extensions = "3.10.0.2"
|
||||
uvicorn = "0.18.1"
|
||||
uvloop = "0.16.0"
|
||||
watchgod = "0.7"
|
||||
websockets = "10.0"
|
||||
zipp = "3.5.0"
|
||||
loguru = "0.5.3"
|
||||
cffi = "1.15.0"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.poetry.scripts]
|
||||
lnbits = "lnbits.server:main"
|
@ -1,52 +1,52 @@
|
||||
aiofiles==0.7.0
|
||||
anyio==3.3.1
|
||||
asgiref==3.4.1
|
||||
aiofiles==0.8.0
|
||||
anyio==3.6.1
|
||||
asyncio==3.4.3
|
||||
attrs==21.2.0
|
||||
attrs==21.4.0
|
||||
bech32==1.2.0
|
||||
bitstring==3.1.9
|
||||
cerberus==1.3.4
|
||||
certifi==2021.5.30
|
||||
charset-normalizer==2.0.6
|
||||
click==8.0.1
|
||||
ecdsa==0.17.0
|
||||
embit==0.4.9
|
||||
environs==9.3.3
|
||||
fastapi==0.68.1
|
||||
certifi==2022.6.15
|
||||
cffi==1.15.0
|
||||
click==8.1.3
|
||||
ecdsa==0.18.0
|
||||
embit==0.5.0
|
||||
environs==9.5.0
|
||||
fastapi==0.79.0
|
||||
h11==0.12.0
|
||||
httpcore==0.15.0
|
||||
httptools==0.2.0
|
||||
httptools==0.4.0
|
||||
httpx==0.23.0
|
||||
idna==3.2
|
||||
importlib-metadata==4.8.1
|
||||
idna==3.3
|
||||
jinja2==3.0.1
|
||||
lnurl==0.3.6
|
||||
loguru==0.6.0
|
||||
markupsafe==2.0.1
|
||||
marshmallow==3.13.0
|
||||
outcome==1.1.0
|
||||
psycopg2-binary==2.9.1
|
||||
pycryptodomex==3.14.1
|
||||
pydantic==1.8.2
|
||||
pypng==0.0.21
|
||||
markupsafe==2.1.1
|
||||
marshmallow==3.17.0
|
||||
outcome==1.2.0
|
||||
packaging==21.3
|
||||
psycopg2-binary==2.9.3
|
||||
pycparser==2.21
|
||||
pycryptodomex==3.15.0
|
||||
pydantic==1.9.1
|
||||
pyngrok==5.1.0
|
||||
pyparsing==3.0.9
|
||||
pypng==0.20220715.0
|
||||
pyqrcode==1.2.1
|
||||
pyscss==1.3.7
|
||||
python-dotenv==0.19.0
|
||||
pyyaml==5.4.1
|
||||
pyscss==1.4.0
|
||||
python-dotenv==0.20.0
|
||||
pyyaml==6.0
|
||||
represent==1.6.0.post0
|
||||
rfc3986==1.5.0
|
||||
secp256k1==0.14.0
|
||||
cffi==1.15.0
|
||||
shortuuid==1.0.1
|
||||
shortuuid==1.0.9
|
||||
six==1.16.0
|
||||
sniffio==1.2.0
|
||||
sqlalchemy-aio==0.17.0
|
||||
sqlalchemy==1.3.23
|
||||
sqlalchemy-aio==0.16.0
|
||||
sse-starlette==0.6.2
|
||||
starlette==0.14.2
|
||||
typing-extensions==3.10.0.2
|
||||
uvicorn==0.15.0
|
||||
sse-starlette==0.10.3
|
||||
starlette==0.19.1
|
||||
typing-extensions==4.3.0
|
||||
uvicorn==0.18.2
|
||||
uvloop==0.16.0
|
||||
watchgod==0.7
|
||||
websockets==10.0
|
||||
zipp==3.5.0
|
||||
watchfiles==0.16.0
|
||||
websockets==10.3
|
||||
|
1
result
Symbolic link
1
result
Symbolic link
@ -0,0 +1 @@
|
||||
/nix/store/ds9c48q7hnkdmpzy3aq14kc1x9wrrszd-python3.9-lnbits-0.1.0
|
@ -1,6 +1,7 @@
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
from lnbits.core.crud import get_wallet
|
||||
from lnbits.core.views.api import api_payment
|
||||
|
||||
from ...helpers import get_random_invoice_data
|
||||
|
||||
@ -155,3 +156,26 @@ async def test_decode_invoice(client, invoice):
|
||||
)
|
||||
assert response.status_code < 300
|
||||
assert response.json()["payment_hash"] == invoice["payment_hash"]
|
||||
|
||||
|
||||
# check api_payment() internal function call (NOT API): payment status
|
||||
@pytest.mark.asyncio
|
||||
async def test_api_payment_without_key(invoice):
|
||||
# check the payment status
|
||||
response = await api_payment(invoice["payment_hash"])
|
||||
assert type(response) == dict
|
||||
assert response["paid"] == True
|
||||
# no key, that's why no "details"
|
||||
assert "details" not in response
|
||||
|
||||
|
||||
# check api_payment() internal function call (NOT API): payment status
|
||||
@pytest.mark.asyncio
|
||||
async def test_api_payment_with_key(invoice, inkey_headers_from):
|
||||
# check the payment status
|
||||
response = await api_payment(
|
||||
invoice["payment_hash"], inkey_headers_from["X-Api-Key"]
|
||||
)
|
||||
assert type(response) == dict
|
||||
assert response["paid"] == True
|
||||
assert "details" in response
|
||||
|
Loading…
x
Reference in New Issue
Block a user