diff --git a/Makefile b/Makefile
index a95fde28..a48fa1a6 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-all: format check
+all: format check lnbits/static/css/base.css requirements.txt
format: prettier black
@@ -18,3 +18,9 @@ checkprettier: $(shell find lnbits -name "*.js" -name ".html")
checkblack: $(shell find lnbits -name "*.py")
./venv/bin/black --check lnbits
+
+lnbits/static/css/base.css: lnbits/static/scss/base.scss
+ ./venv/bin/pyscss -o lnbits/static/css/base.css lnbits/static/scss/base.scss
+
+requirements.txt: Pipfile.lock
+ cat Pipfile.lock | jq -r '.default | map_values(.version) | to_entries | map("\(.key)\(.value)") | join("\n")' > requirements.txt
diff --git a/Pipfile b/Pipfile
index 0962b5dc..1e81675d 100644
--- a/Pipfile
+++ b/Pipfile
@@ -11,15 +11,15 @@ bitstring = "*"
cerberus = "*"
ecdsa = "*"
environs = "*"
-flask = "*"
-flask-assets = "*"
-flask-compress = "*"
-flask-cors = "*"
-flask-talisman = "*"
lnurl = "*"
pyscss = "*"
requests = "*"
shortuuid = "*"
+quart = "*"
+quart-cors = "*"
+quart-compress = "*"
+secure = "*"
+typing-extensions = "*"
[dev-packages]
black = "==20.8b1"
diff --git a/Pipfile.lock b/Pipfile.lock
index f2e187ac..93ae73b6 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "2270f2525e54e976b09491e458033d25ec5bbdea9e74d417e787df33031c6948"
+ "sha256": "2c716474f9f263d8e1310ca44c2f50996f3516273b483a40e2b2ad68b8071dd6"
},
"pipfile-spec": 6,
"requires": {
@@ -16,6 +16,13 @@
]
},
"default": {
+ "aiofiles": {
+ "hashes": [
+ "sha256:377fdf7815cc611870c59cbd07b68b180841d2a2b79812d8c218be02448c2acb",
+ "sha256:98e6bcfd1b50f97db4980e182ddd509b7cc35909e903a8fe50d8849e02d815af"
+ ],
+ "version": "==0.5.0"
+ },
"bech32": {
"hashes": [
"sha256:7d6db8214603bd7871fcfa6c0826ef68b85b0abd90fa21c285a9c5e21d2bd899",
@@ -31,6 +38,12 @@
"index": "pypi",
"version": "==3.1.7"
},
+ "blinker": {
+ "hashes": [
+ "sha256:471aee25f3992bd325afa3772f1063dbdbbca947a041b8b89466dc00d606f8b6"
+ ],
+ "version": "==1.4"
+ },
"brotli": {
"hashes": [
"sha256:160c78292e98d21e73a4cc7f76a234390e516afcd982fa17e1422f7c6a9ce9c8",
@@ -108,44 +121,41 @@
"index": "pypi",
"version": "==8.0.0"
},
- "flask": {
+ "h11": {
"hashes": [
- "sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060",
- "sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557"
+ "sha256:311dc5478c2568cc07262e0381cdfc5b9c6ba19775905736c87e81ae6662b9fd",
+ "sha256:9eecfbafc980976dbff26a01dd3487644dd5d00f8038584451fc64a660f7c502"
],
- "index": "pypi",
- "version": "==1.1.2"
+ "version": "==0.10.0"
},
- "flask-assets": {
+ "h2": {
"hashes": [
- "sha256:1dfdea35e40744d46aada72831f7613d67bf38e8b20ccaaa9e91fdc37aa3b8c2",
- "sha256:2845bd3b479be9db8556801e7ebc2746ce2d9edb4e7b64a1c786ecbfc1e5867b"
+ "sha256:61e0f6601fa709f35cdb730863b4e5ec7ad449792add80d1410d4174ed139af5",
+ "sha256:875f41ebd6f2c44781259005b157faed1a5031df3ae5aa7bcb4628a6c0782f14"
],
- "index": "pypi",
- "version": "==2.0"
+ "version": "==3.2.0"
},
- "flask-compress": {
+ "hpack": {
"hashes": [
- "sha256:f367b2b46003dd62be34f7fb1379938032656dca56377a9bc90e7188e4289a7c"
+ "sha256:0edd79eda27a53ba5be2dfabf3b15780928a0dff6eb0c60a3d6767720e970c89",
+ "sha256:8eec9c1f4bfae3408a3f30500261f7e6a65912dc138526ea054f9ad98892e9d2"
],
- "index": "pypi",
- "version": "==1.5.0"
+ "version": "==3.0.0"
},
- "flask-cors": {
+ "hypercorn": {
"hashes": [
- "sha256:6bcfc100288c5d1bcb1dbb854babd59beee622ffd321e444b05f24d6d58466b8",
- "sha256:cee4480aaee421ed029eaa788f4049e3e26d15b5affb6a880dade6bafad38324"
+ "sha256:19f32e7267225c8108ad585b2c5deddf1fe75950797a0e87a682a3a00ef1af95",
+ "sha256:809d77f3bf9fa0794a598d8dfa0f8d889e7e1c2f927581cd33068803169dc474"
],
- "index": "pypi",
- "version": "==3.0.9"
+ "markers": "python_version >= '3.7'",
+ "version": "==0.10.2"
},
- "flask-talisman": {
+ "hyperframe": {
"hashes": [
- "sha256:468131464a249274ed226efc21b372518f442487e58918ccab8357eaa638fd1f",
- "sha256:eaa754f4b771dfbe473843391d69643b79e3a38c865790011ac5e4179c68e3ec"
+ "sha256:5187962cb16dcc078f23cb5a4b110098d546c3f41ff2d4038a9896893bbd0b40",
+ "sha256:a9f5c17f2cc3c719b917c4f33ed1c61bd1f8dfac4b1bd23b7c80b3400971b41f"
],
- "index": "pypi",
- "version": "==0.7.0"
+ "version": "==5.2.0"
},
"idna": {
"hashes": [
@@ -225,6 +235,13 @@
"markers": "python_version >= '3.5'",
"version": "==3.7.1"
},
+ "priority": {
+ "hashes": [
+ "sha256:6bc1961a6d7fcacbfc337769f1a382c8e746566aaa365e78047abe9f66b2ffbe",
+ "sha256:be4fcb94b5e37cdeb40af5533afe6dd603bd665fe9c8b3052610fc1001d5d1eb"
+ ],
+ "version": "==1.3.0"
+ },
"pydantic": {
"hashes": [
"sha256:1783c1d927f9e1366e0e0609ae324039b2479a1a282a98ed6a6836c9ed02002c",
@@ -262,6 +279,30 @@
],
"version": "==0.14.0"
},
+ "quart": {
+ "hashes": [
+ "sha256:9c634e4c1e4b21b824003c676de1583581258c72b0ac4d2ba747db846e97ff56",
+ "sha256:d885d782edd9d5dcfd2c4a56e020db3b82493d4c3950f91c221b7d88d239ac93"
+ ],
+ "index": "pypi",
+ "version": "==0.13.1"
+ },
+ "quart-compress": {
+ "hashes": [
+ "sha256:41cd0cc8d26905a45025ddda7022461a71b9d1d950b21b006dc106a1c41c75ef",
+ "sha256:63af5e6370aa7850fb219d22e1db89965aeb13b8f27bc83e7f9a44118faa3c54"
+ ],
+ "index": "pypi",
+ "version": "==0.2.1"
+ },
+ "quart-cors": {
+ "hashes": [
+ "sha256:020a17d504264db86cada3c1335ef174af28b33f57cee321ddc46d69c33d5c8e",
+ "sha256:c08bdb326219b6c186d19ed6a97a7fd02de8fe36c7856af889494c69b525c53c"
+ ],
+ "index": "pypi",
+ "version": "==0.3.0"
+ },
"requests": {
"hashes": [
"sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b",
@@ -270,6 +311,14 @@
"index": "pypi",
"version": "==2.24.0"
},
+ "secure": {
+ "hashes": [
+ "sha256:4dc8dd4b548831c3ad7f94079332c41d67c781eccc32215ff5a8a49582c1a447",
+ "sha256:b3bf1e39ebf40040fc3248392343a5052aa14cb45fc87ec91b0bd11f19cc46bd"
+ ],
+ "index": "pypi",
+ "version": "==0.2.1"
+ },
"shortuuid": {
"hashes": [
"sha256:3c11d2007b915c43bee3e10625f068d8a349e04f0d81f08f5fa08507427ebf1f",
@@ -286,13 +335,20 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.15.0"
},
+ "toml": {
+ "hashes": [
+ "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f",
+ "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"
+ ],
+ "version": "==0.10.1"
+ },
"typing-extensions": {
"hashes": [
"sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918",
"sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c",
"sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"
],
- "markers": "python_version < '3.8'",
+ "index": "pypi",
"version": "==3.7.4.3"
},
"urllib3": {
@@ -303,13 +359,6 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
"version": "==1.25.10"
},
- "webassets": {
- "hashes": [
- "sha256:167132337677c8cedc9705090f6d48da3fb262c8e0b2773b29f3352f050181cd",
- "sha256:a31a55147752ba1b3dc07dee0ad8c8efff274464e08bbdb88c1fd59ffd552724"
- ],
- "version": "==2.0"
- },
"werkzeug": {
"hashes": [
"sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43",
@@ -317,6 +366,14 @@
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==1.0.1"
+ },
+ "wsproto": {
+ "hashes": [
+ "sha256:614798c30e5dc2b3f65acc03d2d50842b97621487350ce79a80a711229edfa9d",
+ "sha256:e3d190a11d9307112ba23bbe60055604949b172143969c8f641318476a9b6f1d"
+ ],
+ "markers": "python_full_version >= '3.6.1'",
+ "version": "==0.15.0"
}
},
"develop": {
@@ -329,11 +386,11 @@
},
"attrs": {
"hashes": [
- "sha256:0ef97238856430dcf9228e07f316aefc17e8939fc8507e18c6501b761ef1a42a",
- "sha256:2867b7b9f8326499ab5b0e2d12801fa5c98842d2cbd22b35112ae04bf85b4dff"
+ "sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594",
+ "sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
- "version": "==20.1.0"
+ "version": "==20.2.0"
},
"black": {
"hashes": [
@@ -353,43 +410,43 @@
},
"coverage": {
"hashes": [
- "sha256:098a703d913be6fbd146a8c50cc76513d726b022d170e5e98dc56d958fd592fb",
- "sha256:16042dc7f8e632e0dcd5206a5095ebd18cb1d005f4c89694f7f8aafd96dd43a3",
- "sha256:1adb6be0dcef0cf9434619d3b892772fdb48e793300f9d762e480e043bd8e716",
- "sha256:27ca5a2bc04d68f0776f2cdcb8bbd508bbe430a7bf9c02315cd05fb1d86d0034",
- "sha256:28f42dc5172ebdc32622a2c3f7ead1b836cdbf253569ae5673f499e35db0bac3",
- "sha256:2fcc8b58953d74d199a1a4d633df8146f0ac36c4e720b4a1997e9b6327af43a8",
- "sha256:304fbe451698373dc6653772c72c5d5e883a4aadaf20343592a7abb2e643dae0",
- "sha256:30bc103587e0d3df9e52cd9da1dd915265a22fad0b72afe54daf840c984b564f",
- "sha256:40f70f81be4d34f8d491e55936904db5c527b0711b2a46513641a5729783c2e4",
- "sha256:4186fc95c9febeab5681bc3248553d5ec8c2999b8424d4fc3a39c9cba5796962",
- "sha256:46794c815e56f1431c66d81943fa90721bb858375fb36e5903697d5eef88627d",
- "sha256:4869ab1c1ed33953bb2433ce7b894a28d724b7aa76c19b11e2878034a4e4680b",
- "sha256:4f6428b55d2916a69f8d6453e48a505c07b2245653b0aa9f0dee38785939f5e4",
- "sha256:52f185ffd3291196dc1aae506b42e178a592b0b60a8610b108e6ad892cfc1bb3",
- "sha256:538f2fd5eb64366f37c97fdb3077d665fa946d2b6d95447622292f38407f9258",
- "sha256:64c4f340338c68c463f1b56e3f2f0423f7b17ba6c3febae80b81f0e093077f59",
- "sha256:675192fca634f0df69af3493a48224f211f8db4e84452b08d5fcebb9167adb01",
- "sha256:700997b77cfab016533b3e7dbc03b71d33ee4df1d79f2463a318ca0263fc29dd",
- "sha256:8505e614c983834239f865da2dd336dcf9d72776b951d5dfa5ac36b987726e1b",
- "sha256:962c44070c281d86398aeb8f64e1bf37816a4dfc6f4c0f114756b14fc575621d",
- "sha256:9e536783a5acee79a9b308be97d3952b662748c4037b6a24cbb339dc7ed8eb89",
- "sha256:9ea749fd447ce7fb1ac71f7616371f04054d969d412d37611716721931e36efd",
- "sha256:a34cb28e0747ea15e82d13e14de606747e9e484fb28d63c999483f5d5188e89b",
- "sha256:a3ee9c793ffefe2944d3a2bd928a0e436cd0ac2d9e3723152d6fd5398838ce7d",
- "sha256:aab75d99f3f2874733946a7648ce87a50019eb90baef931698f96b76b6769a46",
- "sha256:b1ed2bdb27b4c9fc87058a1cb751c4df8752002143ed393899edb82b131e0546",
- "sha256:b360d8fd88d2bad01cb953d81fd2edd4be539df7bfec41e8753fe9f4456a5082",
- "sha256:b8f58c7db64d8f27078cbf2a4391af6aa4e4767cc08b37555c4ae064b8558d9b",
- "sha256:c1bbb628ed5192124889b51204de27c575b3ffc05a5a91307e7640eff1d48da4",
- "sha256:c2ff24df02a125b7b346c4c9078c8936da06964cc2d276292c357d64378158f8",
- "sha256:c890728a93fffd0407d7d37c1e6083ff3f9f211c83b4316fae3778417eab9811",
- "sha256:c96472b8ca5dc135fb0aa62f79b033f02aa434fb03a8b190600a5ae4102df1fd",
- "sha256:ce7866f29d3025b5b34c2e944e66ebef0d92e4a4f2463f7266daa03a1332a651",
- "sha256:e26c993bd4b220429d4ec8c1468eca445a4064a61c74ca08da7429af9bc53bb0"
+ "sha256:0203acd33d2298e19b57451ebb0bed0ab0c602e5cf5a818591b4918b1f97d516",
+ "sha256:0f313707cdecd5cd3e217fc68c78a960b616604b559e9ea60cc16795c4304259",
+ "sha256:1c6703094c81fa55b816f5ae542c6ffc625fec769f22b053adb42ad712d086c9",
+ "sha256:1d44bb3a652fed01f1f2c10d5477956116e9b391320c94d36c6bf13b088a1097",
+ "sha256:280baa8ec489c4f542f8940f9c4c2181f0306a8ee1a54eceba071a449fb870a0",
+ "sha256:29a6272fec10623fcbe158fdf9abc7a5fa032048ac1d8631f14b50fbfc10d17f",
+ "sha256:2b31f46bf7b31e6aa690d4c7a3d51bb262438c6dcb0d528adde446531d0d3bb7",
+ "sha256:2d43af2be93ffbad25dd959899b5b809618a496926146ce98ee0b23683f8c51c",
+ "sha256:381ead10b9b9af5f64646cd27107fb27b614ee7040bb1226f9c07ba96625cbb5",
+ "sha256:47a11bdbd8ada9b7ee628596f9d97fbd3851bd9999d398e9436bd67376dbece7",
+ "sha256:4d6a42744139a7fa5b46a264874a781e8694bb32f1d76d8137b68138686f1729",
+ "sha256:50691e744714856f03a86df3e2bff847c2acede4c191f9a1da38f088df342978",
+ "sha256:530cc8aaf11cc2ac7430f3614b04645662ef20c348dce4167c22d99bec3480e9",
+ "sha256:582ddfbe712025448206a5bc45855d16c2e491c2dd102ee9a2841418ac1c629f",
+ "sha256:63808c30b41f3bbf65e29f7280bf793c79f54fb807057de7e5238ffc7cc4d7b9",
+ "sha256:71b69bd716698fa62cd97137d6f2fdf49f534decb23a2c6fc80813e8b7be6822",
+ "sha256:7858847f2d84bf6e64c7f66498e851c54de8ea06a6f96a32a1d192d846734418",
+ "sha256:78e93cc3571fd928a39c0b26767c986188a4118edc67bc0695bc7a284da22e82",
+ "sha256:7f43286f13d91a34fadf61ae252a51a130223c52bfefb50310d5b2deb062cf0f",
+ "sha256:86e9f8cd4b0cdd57b4ae71a9c186717daa4c5a99f3238a8723f416256e0b064d",
+ "sha256:8f264ba2701b8c9f815b272ad568d555ef98dfe1576802ab3149c3629a9f2221",
+ "sha256:9342dd70a1e151684727c9c91ea003b2fb33523bf19385d4554f7897ca0141d4",
+ "sha256:9361de40701666b034c59ad9e317bae95c973b9ff92513dd0eced11c6adf2e21",
+ "sha256:9669179786254a2e7e57f0ecf224e978471491d660aaca833f845b72a2df3709",
+ "sha256:aac1ba0a253e17889550ddb1b60a2063f7474155465577caa2a3b131224cfd54",
+ "sha256:aef72eae10b5e3116bac6957de1df4d75909fc76d1499a53fb6387434b6bcd8d",
+ "sha256:bd3166bb3b111e76a4f8e2980fa1addf2920a4ca9b2b8ca36a3bc3dedc618270",
+ "sha256:c1b78fb9700fc961f53386ad2fd86d87091e06ede5d118b8a50dea285a071c24",
+ "sha256:c3888a051226e676e383de03bf49eb633cd39fc829516e5334e69b8d81aae751",
+ "sha256:c5f17ad25d2c1286436761b462e22b5020d83316f8e8fcb5deb2b3151f8f1d3a",
+ "sha256:c851b35fc078389bc16b915a0a7c1d5923e12e2c5aeec58c52f4aa8085ac8237",
+ "sha256:cb7df71de0af56000115eafd000b867d1261f786b5eebd88a0ca6360cccfaca7",
+ "sha256:cedb2f9e1f990918ea061f28a0f0077a07702e3819602d3507e2ff98c8d20636",
+ "sha256:e8caf961e1b1a945db76f1b5fa9c91498d15f545ac0ababbe575cfab185d3bd8"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
- "version": "==5.2.1"
+ "version": "==5.3"
},
"flake8": {
"hashes": [
@@ -407,14 +464,6 @@
"index": "pypi",
"version": "==17.8.0"
},
- "importlib-metadata": {
- "hashes": [
- "sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83",
- "sha256:dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070"
- ],
- "markers": "python_version < '3.8'",
- "version": "==1.7.0"
- },
"iniconfig": {
"hashes": [
"sha256:80cf40c597eb564e86346103f609d74efce0f6b4d4f30ec8ce9e2c26411ba437",
@@ -521,11 +570,11 @@
},
"pytest": {
"hashes": [
- "sha256:85228d75db9f45e06e57ef9bf4429267f81ac7c0d742cc9ed63d09886a9fe6f4",
- "sha256:8b6007800c53fdacd5a5c192203f4e531eb2a1540ad9c752e052ec0f7143dbad"
+ "sha256:0e37f61339c4578776e090c3b8f6b16ce4db333889d65d0efb305243ec544b40",
+ "sha256:c8f57c2a30983f469bf03e68cdfa74dc474ce56b8f280ddcb080dfd91df01043"
],
"index": "pypi",
- "version": "==6.0.1"
+ "version": "==6.0.2"
},
"pytest-cov": {
"hashes": [
@@ -608,16 +657,8 @@
"sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c",
"sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"
],
- "markers": "python_version < '3.8'",
+ "index": "pypi",
"version": "==3.7.4.3"
- },
- "zipp": {
- "hashes": [
- "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b",
- "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"
- ],
- "markers": "python_version >= '3.6'",
- "version": "==3.1.0"
}
}
}
diff --git a/app.json b/app.json
index 96a3f11e..a08f9f39 100644
--- a/app.json
+++ b/app.json
@@ -1,7 +1,5 @@
{
"scripts": {
- "dokku": {
- "predeploy": "flask migrate"
- }
+ "dokku": {}
}
}
diff --git a/docs/devs/installation.md b/docs/devs/installation.md
index c4aad25e..da53cc3e 100644
--- a/docs/devs/installation.md
+++ b/docs/devs/installation.md
@@ -42,23 +42,19 @@ Take a look at [Polar][polar] for an excellent way of spinning up a Lightning Ne
Running the server
------------------
-LNbits uses [Flask][flask] as an application server.
+LNbits uses [Quart][quart] as an application server.
```sh
$ pipenv run python -m lnbits
```
-There is an environment variable called `FLASK_ENV` that has to be set to `development`
-if you want to run Flask in debug mode with autoreload
-
-
Frontend
--------
The frontend uses [Vue.js and Quasar][quasar].
-[flask]: http://flask.pocoo.org/
+[quart]: https://pgjones.gitlab.io/
[pipenv]: https://pipenv.pypa.io/
[polar]: https://lightningpolar.com/
[quasar]: https://quasar.dev/start/how-to-use-vue
diff --git a/lnbits/app.py b/lnbits/app.py
index bf866b59..31edcc14 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -1,27 +1,28 @@
import importlib
-from flask import Flask, g
-from flask_assets import Bundle # type: ignore
-from flask_cors import CORS # type: ignore
-from flask_talisman import Talisman # type: ignore
-from werkzeug.middleware.proxy_fix import ProxyFix
+from quart import Quart, g
+from quart_cors import cors # type: ignore
+from quart_compress import Compress # type: ignore
+from secure import SecureHeaders # type: ignore
-from .commands import flask_migrate
+from .commands import db_migrate
from .core import core_app
from .db import open_db
-from .ext import assets, compress
from .helpers import get_valid_extensions
+secure_headers = SecureHeaders(hsts=False)
-def create_app(config_object="lnbits.settings") -> Flask:
- """Create application factory, as explained here: http://flask.pocoo.org/docs/patterns/appfactories/.
+
+def create_app(config_object="lnbits.settings") -> Quart:
+ """Create application factory.
:param config_object: The configuration object to use.
"""
- app = Flask(__name__, static_folder="static")
- app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1) # type: ignore
+ app = Quart(__name__, static_folder="static")
app.config.from_object(config_object)
- register_flask_extensions(app)
+ cors(app)
+ Compress(app)
+
register_blueprints(app)
register_filters(app)
register_commands(app)
@@ -44,35 +45,11 @@ def register_blueprints(app) -> None:
def register_commands(app):
"""Register Click commands."""
- app.cli.add_command(flask_migrate)
-
-
-def register_flask_extensions(app):
- """Register Flask extensions."""
- """If possible we use the .init_app() option so that Blueprints can also use extensions."""
- CORS(app)
- Talisman(
- app,
- force_https=app.config["FORCE_HTTPS"],
- content_security_policy={
- "default-src": [
- "'self'",
- "'unsafe-eval'",
- "'unsafe-inline'",
- "blob:",
- "api.opennode.co",
- ]
- },
- )
-
- assets.init_app(app)
- assets.register("base_css", Bundle("scss/base.scss", filters="pyscss", output="css/base.css"))
- compress.init_app(app)
+ app.cli.add_command(db_migrate)
def register_filters(app):
"""Jinja filters."""
- app.jinja_env.globals["DEBUG"] = app.config["DEBUG"]
app.jinja_env.globals["EXTENSIONS"] = get_valid_extensions()
app.jinja_env.globals["SITE_TITLE"] = app.config["LNBITS_SITE_TITLE"]
@@ -81,9 +58,14 @@ def register_request_hooks(app):
"""Open the core db for each request so everything happens in a big transaction"""
@app.before_request
- def before_request():
+ async def before_request():
g.db = open_db()
+ @app.after_request
+ async def set_secure_headers(response):
+ secure_headers.quart(response)
+ return response
+
@app.teardown_request
- def after_request(exc):
+ async def after_request(exc):
g.db.__exit__(type(exc), exc, None)
diff --git a/lnbits/commands.py b/lnbits/commands.py
index b6be6f30..60170a90 100644
--- a/lnbits/commands.py
+++ b/lnbits/commands.py
@@ -9,7 +9,7 @@ from .helpers import get_valid_extensions
@click.command("migrate")
-def flask_migrate():
+def db_migrate():
migrate_databases()
diff --git a/lnbits/core/__init__.py b/lnbits/core/__init__.py
index 5af72f9f..e35d66d2 100644
--- a/lnbits/core/__init__.py
+++ b/lnbits/core/__init__.py
@@ -1,7 +1,9 @@
-from flask import Blueprint
+from quart import Blueprint
-core_app: Blueprint = Blueprint("core", __name__, template_folder="templates", static_folder="static")
+core_app: Blueprint = Blueprint(
+ "core", __name__, template_folder="templates", static_folder="static", static_url_path="/core/static"
+)
from .views.api import * # noqa
diff --git a/lnbits/core/crud.py b/lnbits/core/crud.py
index 35f10193..6d19c904 100644
--- a/lnbits/core/crud.py
+++ b/lnbits/core/crud.py
@@ -2,7 +2,7 @@ import json
import datetime
from uuid import uuid4
from typing import List, Optional, Dict
-from flask import g
+from quart import g
from lnbits import bolt11
from lnbits.settings import DEFAULT_WALLET_NAME
diff --git a/lnbits/core/services.py b/lnbits/core/services.py
index fd42aa6a..e16b2f21 100644
--- a/lnbits/core/services.py
+++ b/lnbits/core/services.py
@@ -1,5 +1,5 @@
from typing import Optional, Tuple, Dict
-from flask import g
+from quart import g
try:
from typing import TypedDict # type: ignore
diff --git a/lnbits/core/templates/core/extensions.html b/lnbits/core/templates/core/extensions.html
index 8c63c49d..8f7e92bd 100644
--- a/lnbits/core/templates/core/extensions.html
+++ b/lnbits/core/templates/core/extensions.html
@@ -1,8 +1,7 @@
{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
-%} {% block scripts %} {{ window_vars(user) }} {% assets filters='rjsmin',
-output='__bundle__/core/extensions.js', 'core/js/extensions.js' %}
-
-{% endassets %} {% endblock %} {% block page %}
+%} {% block scripts %} {{ window_vars(user) }}
+
+{% endblock %} {% block page %}
-{% endassets %} {% endblock %} {% block page %}
+{% extends "public.html" %} {% block scripts %}
+
+{% endblock %} {% block page %}
diff --git a/lnbits/core/templates/core/wallet.html b/lnbits/core/templates/core/wallet.html
index b3370bcc..5c286ba6 100644
--- a/lnbits/core/templates/core/wallet.html
+++ b/lnbits/core/templates/core/wallet.html
@@ -1,21 +1,7 @@
{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
-%} {% block styles %}
-
-{% endblock %} {% block scripts %} {{ window_vars(user, wallet) }}
-
-{% assets filters='rjsmin', output='__bundle__/core/chart.js',
-'vendor/moment@2.27.0/moment.min.js', 'vendor/chart.js@2.9.3/chart.min.js' %}
-
-{% endassets %} {% assets filters='rjsmin', output='__bundle__/core/wallet.js',
-'vendor/bolt11/utils.js', 'vendor/bolt11/decoder.js',
-'vendor/vue-qrcode-reader@2.2.0/vue-qrcode-reader.min.js', 'core/js/wallet.js'
-%}
-
-{% endassets %} {% endblock %} {% block page %}
+%} {% block scripts %} {{ window_vars(user, wallet) }}
+
+{% endblock %} {% block page %}
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index e4cb4fa4..3ca6eff4 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -1,4 +1,4 @@
-from flask import g, jsonify, request
+from quart import g, jsonify, request
from http import HTTPStatus
from binascii import unhexlify
@@ -12,7 +12,7 @@ from lnbits.settings import WALLET
@core_app.route("/api/v1/payments", methods=["GET"])
@api_check_wallet_key("invoice")
-def api_payments():
+async def api_payments():
if "check_pending" in request.args:
delete_expired_invoices()
@@ -33,7 +33,7 @@ def api_payments():
"description_hash": {"type": "string", "empty": False, "required": True, "excludes": "memo"},
}
)
-def api_payments_create_invoice():
+async def api_payments_create_invoice():
if "description_hash" in g.data:
description_hash = unhexlify(g.data["description_hash"])
memo = ""
@@ -65,7 +65,7 @@ def api_payments_create_invoice():
@api_check_wallet_key("admin")
@api_validate_post_request(schema={"bolt11": {"type": "string", "empty": False, "required": True}})
-def api_payments_pay_invoice():
+async def api_payments_pay_invoice():
try:
payment_hash = pay_invoice(wallet_id=g.wallet.id, payment_request=g.data["bolt11"])
except ValueError as e:
@@ -91,15 +91,15 @@ def api_payments_pay_invoice():
@core_app.route("/api/v1/payments", methods=["POST"])
@api_validate_post_request(schema={"out": {"type": "boolean", "required": True}})
-def api_payments_create():
+async def api_payments_create():
if g.data["out"] is True:
- return api_payments_pay_invoice()
- return api_payments_create_invoice()
+ return await api_payments_pay_invoice()
+ return await api_payments_create_invoice()
@core_app.route("/api/v1/payments/", methods=["GET"])
@api_check_wallet_key("invoice")
-def api_payment(payment_hash):
+async def api_payment(payment_hash):
payment = g.wallet.get_payment(payment_hash)
if not payment:
diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py
index 65b1dbf0..36720d90 100644
--- a/lnbits/core/views/generic.py
+++ b/lnbits/core/views/generic.py
@@ -1,4 +1,4 @@
-from flask import g, abort, redirect, request, render_template, send_from_directory, url_for
+from quart import g, abort, redirect, request, render_template, send_from_directory, url_for
from http import HTTPStatus
from os import path
@@ -16,19 +16,19 @@ from ..crud import (
@core_app.route("/favicon.ico")
-def favicon():
- return send_from_directory(path.join(core_app.root_path, "static"), "favicon.ico")
+async def favicon():
+ return await send_from_directory(path.join(core_app.root_path, "static"), "favicon.ico")
@core_app.route("/")
-def home():
- return render_template("core/index.html", lnurl=request.args.get("lightning", None))
+async def home():
+ return await render_template("core/index.html", lnurl=request.args.get("lightning", None))
@core_app.route("/extensions")
@validate_uuids(["usr"], required=True)
@check_user_exists()
-def extensions():
+async def extensions():
extension_to_enable = request.args.get("enable", type=str)
extension_to_disable = request.args.get("disable", type=str)
@@ -40,12 +40,12 @@ def extensions():
elif extension_to_disable:
update_user_extension(user_id=g.user.id, extension=extension_to_disable, active=0)
- return render_template("core/extensions.html", user=get_user(g.user.id))
+ return await render_template("core/extensions.html", user=get_user(g.user.id))
@core_app.route("/wallet")
@validate_uuids(["usr", "wal"])
-def wallet():
+async def wallet():
user_id = request.args.get("usr", type=str)
wallet_id = request.args.get("wal", type=str)
wallet_name = request.args.get("nme", type=str)
@@ -76,13 +76,15 @@ def wallet():
if wallet_id not in user.wallet_ids:
abort(HTTPStatus.FORBIDDEN, "Not your wallet.")
- return render_template("core/wallet.html", user=user, wallet=user.get_wallet(wallet_id), service_fee=service_fee)
+ return await render_template(
+ "core/wallet.html", user=user, wallet=user.get_wallet(wallet_id), service_fee=service_fee
+ )
@core_app.route("/deletewallet")
@validate_uuids(["usr", "wal"], required=True)
@check_user_exists()
-def deletewallet():
+async def deletewallet():
wallet_id = request.args.get("wal", type=str)
user_wallet_ids = g.user.wallet_ids
diff --git a/lnbits/core/views/lnurl.py b/lnbits/core/views/lnurl.py
index 97cbe2cc..3ec0fc05 100644
--- a/lnbits/core/views/lnurl.py
+++ b/lnbits/core/views/lnurl.py
@@ -1,6 +1,6 @@
import requests
-from flask import abort, redirect, request, url_for
+from quart import abort, redirect, request, url_for
from http import HTTPStatus
from lnurl import LnurlWithdrawResponse, handle as handle_lnurl # type: ignore
from lnurl.exceptions import LnurlException # type: ignore
@@ -13,7 +13,7 @@ from ..crud import create_account, get_user, create_wallet, create_payment
@core_app.route("/lnurlwallet")
-def lnurlwallet():
+async def lnurlwallet():
memo = "LNbits LNURL funding"
try:
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index 8f09089c..ac73e4e4 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -1,5 +1,5 @@
from cerberus import Validator # type: ignore
-from flask import g, abort, jsonify, request
+from quart import g, abort, jsonify, request
from functools import wraps
from http import HTTPStatus
from typing import List, Union
@@ -12,7 +12,7 @@ from lnbits.settings import LNBITS_ALLOWED_USERS
def api_check_wallet_key(key_type: str = "invoice"):
def wrap(view):
@wraps(view)
- def wrapped_view(**kwargs):
+ async def wrapped_view(**kwargs):
try:
g.wallet = get_wallet_for_key(request.headers["X-Api-Key"], key_type)
except KeyError:
@@ -24,7 +24,7 @@ def api_check_wallet_key(key_type: str = "invoice"):
if not g.wallet:
return jsonify({"message": "Wrong keys."}), HTTPStatus.UNAUTHORIZED
- return view(**kwargs)
+ return await view(**kwargs)
return wrapped_view
@@ -34,7 +34,7 @@ def api_check_wallet_key(key_type: str = "invoice"):
def api_validate_post_request(*, schema: dict):
def wrap(view):
@wraps(view)
- def wrapped_view(**kwargs):
+ async def wrapped_view(**kwargs):
if "application/json" not in request.headers["Content-Type"]:
return (
jsonify({"message": "Content-Type must be `application/json`."}),
@@ -42,7 +42,8 @@ def api_validate_post_request(*, schema: dict):
)
v = Validator(schema)
- g.data = {key: request.json[key] for key in schema.keys() if key in request.json}
+ data = await request.get_json()
+ g.data = {key: data[key] for key in schema.keys() if key in data}
if not v.validate(g.data):
return (
@@ -50,7 +51,7 @@ def api_validate_post_request(*, schema: dict):
HTTPStatus.BAD_REQUEST,
)
- return view(**kwargs)
+ return await view(**kwargs)
return wrapped_view
@@ -60,13 +61,13 @@ def api_validate_post_request(*, schema: dict):
def check_user_exists(param: str = "usr"):
def wrap(view):
@wraps(view)
- def wrapped_view(**kwargs):
+ async def wrapped_view(**kwargs):
g.user = get_user(request.args.get(param, type=str)) or abort(HTTPStatus.NOT_FOUND, "User does not exist.")
if LNBITS_ALLOWED_USERS and g.user.id not in LNBITS_ALLOWED_USERS:
abort(HTTPStatus.UNAUTHORIZED, "User not authorized.")
- return view(**kwargs)
+ return await view(**kwargs)
return wrapped_view
@@ -76,7 +77,7 @@ def check_user_exists(param: str = "usr"):
def validate_uuids(params: List[str], *, required: Union[bool, List[str]] = False, version: int = 4):
def wrap(view):
@wraps(view)
- def wrapped_view(**kwargs):
+ async def wrapped_view(**kwargs):
query_params = {param: request.args.get(param, type=str) for param in params}
for param, value in query_params.items():
@@ -89,7 +90,7 @@ def validate_uuids(params: List[str], *, required: Union[bool, List[str]] = Fals
except ValueError:
abort(HTTPStatus.BAD_REQUEST, f"`{param}` is not a valid UUID.")
- return view(**kwargs)
+ return await view(**kwargs)
return wrapped_view
diff --git a/lnbits/ext.py b/lnbits/ext.py
deleted file mode 100644
index 74e384b4..00000000
--- a/lnbits/ext.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from flask_assets import Environment # type: ignore
-from flask_compress import Compress # type: ignore
-
-
-assets = Environment()
-compress = Compress()
diff --git a/lnbits/extensions/amilk/__init__.py b/lnbits/extensions/amilk/__init__.py
index ea93c989..182f0235 100644
--- a/lnbits/extensions/amilk/__init__.py
+++ b/lnbits/extensions/amilk/__init__.py
@@ -1,4 +1,4 @@
-from flask import Blueprint
+from quart import Blueprint
amilk_ext: Blueprint = Blueprint("amilk", __name__, static_folder="static", template_folder="templates")
diff --git a/lnbits/extensions/amilk/views.py b/lnbits/extensions/amilk/views.py
index 11c86f0b..fa214e32 100644
--- a/lnbits/extensions/amilk/views.py
+++ b/lnbits/extensions/amilk/views.py
@@ -1,4 +1,4 @@
-from flask import g, abort, render_template
+from quart import g, abort, render_template
from http import HTTPStatus
from lnbits.decorators import check_user_exists, validate_uuids
@@ -10,12 +10,11 @@ from .crud import get_amilk
@amilk_ext.route("/")
@validate_uuids(["usr"], required=True)
@check_user_exists()
-def index():
- return render_template("amilk/index.html", user=g.user)
+async def index():
+ return await render_template("amilk/index.html", user=g.user)
@amilk_ext.route("/")
-def wall(amilk_id):
+async def wall(amilk_id):
amilk = get_amilk(amilk_id) or abort(HTTPStatus.NOT_FOUND, "AMilk does not exist.")
-
- return render_template("amilk/wall.html", amilk=amilk)
+ return await render_template("amilk/wall.html", amilk=amilk)
diff --git a/lnbits/extensions/amilk/views_api.py b/lnbits/extensions/amilk/views_api.py
index 816ca99e..c61a4fc1 100644
--- a/lnbits/extensions/amilk/views_api.py
+++ b/lnbits/extensions/amilk/views_api.py
@@ -1,5 +1,5 @@
import requests
-from flask import g, jsonify, request, abort
+from quart import g, jsonify, request, abort
from http import HTTPStatus
from lnurl import LnurlWithdrawResponse, handle as handle_lnurl
from lnurl.exceptions import LnurlException
@@ -15,7 +15,7 @@ from .crud import create_amilk, get_amilk, get_amilks, delete_amilk
@amilk_ext.route("/api/v1/amilk", methods=["GET"])
@api_check_wallet_key("invoice")
-def api_amilks():
+async def api_amilks():
wallet_ids = [g.wallet.id]
if "all_wallets" in request.args:
@@ -25,7 +25,7 @@ def api_amilks():
@amilk_ext.route("/api/v1/amilk/milk/", methods=["GET"])
-def api_amilkit(amilk_id):
+async def api_amilkit(amilk_id):
milk = get_amilk(amilk_id)
memo = milk.id
@@ -66,7 +66,7 @@ def api_amilkit(amilk_id):
"amount": {"type": "integer", "min": 0, "required": True},
}
)
-def api_amilk_create():
+async def api_amilk_create():
amilk = create_amilk(wallet_id=g.wallet.id, lnurl=g.data["lnurl"], atime=g.data["atime"], amount=g.data["amount"])
return jsonify(amilk._asdict()), HTTPStatus.CREATED
@@ -74,7 +74,7 @@ def api_amilk_create():
@amilk_ext.route("/api/v1/amilk/", methods=["DELETE"])
@api_check_wallet_key("invoice")
-def api_amilk_delete(amilk_id):
+async def api_amilk_delete(amilk_id):
amilk = get_amilk(amilk_id)
if not amilk:
diff --git a/lnbits/extensions/diagonalley/__init__.py b/lnbits/extensions/diagonalley/__init__.py
index c3eaf52a..41afebb6 100644
--- a/lnbits/extensions/diagonalley/__init__.py
+++ b/lnbits/extensions/diagonalley/__init__.py
@@ -1,4 +1,4 @@
-from flask import Blueprint
+from quart import Blueprint
diagonalley_ext: Blueprint = Blueprint("diagonalley", __name__, static_folder="static", template_folder="templates")
diff --git a/lnbits/extensions/diagonalley/views.py b/lnbits/extensions/diagonalley/views.py
index 41a19f5a..6781a99e 100644
--- a/lnbits/extensions/diagonalley/views.py
+++ b/lnbits/extensions/diagonalley/views.py
@@ -1,15 +1,11 @@
-import json
-
-from flask import g, abort, render_template, jsonify
+from quart import g, render_template
from lnbits.decorators import check_user_exists, validate_uuids
from lnbits.extensions.diagonalley import diagonalley_ext
-from lnbits.db import open_ext_db
@diagonalley_ext.route("/")
@validate_uuids(["usr"], required=True)
@check_user_exists()
-def index():
-
- return render_template("diagonalley/index.html", user=g.user)
+async def index():
+ return await render_template("diagonalley/index.html", user=g.user)
diff --git a/lnbits/extensions/diagonalley/views_api.py b/lnbits/extensions/diagonalley/views_api.py
index fbdc2a7b..9ded7c2c 100644
--- a/lnbits/extensions/diagonalley/views_api.py
+++ b/lnbits/extensions/diagonalley/views_api.py
@@ -1,4 +1,4 @@
-from flask import g, jsonify, request
+from quart import g, jsonify, request
from http import HTTPStatus
from lnbits.core.crud import get_user
@@ -18,19 +18,19 @@ from .crud import (
create_diagonalleys_order,
get_diagonalleys_order,
get_diagonalleys_orders,
- delete_diagonalleys_order,
+ update_diagonalleys_product,
)
from lnbits.core.services import create_invoice
from base64 import urlsafe_b64encode
from uuid import uuid4
from lnbits.db import open_ext_db
-###Products
+### Products
@diagonalley_ext.route("/api/v1/diagonalley/products", methods=["GET"])
@api_check_wallet_key(key_type="invoice")
-def api_diagonalley_products():
+async def api_diagonalley_products():
wallet_ids = [g.wallet.id]
if "all_wallets" in request.args:
@@ -52,7 +52,7 @@ def api_diagonalley_products():
"quantity": {"type": "integer", "min": 0, "required": True},
}
)
-def api_diagonalley_product_create(product_id=None):
+async def api_diagonalley_product_create(product_id=None):
if product_id:
product = get_diagonalleys_indexer(product_id)
@@ -72,7 +72,7 @@ def api_diagonalley_product_create(product_id=None):
@diagonalley_ext.route("/api/v1/diagonalley/products/", methods=["DELETE"])
@api_check_wallet_key(key_type="invoice")
-def api_diagonalley_products_delete(product_id):
+async def api_diagonalley_products_delete(product_id):
product = get_diagonalleys_product(product_id)
if not product:
@@ -91,7 +91,7 @@ def api_diagonalley_products_delete(product_id):
@diagonalley_ext.route("/api/v1/diagonalley/indexers", methods=["GET"])
@api_check_wallet_key(key_type="invoice")
-def api_diagonalley_indexers():
+async def api_diagonalley_indexers():
wallet_ids = [g.wallet.id]
if "all_wallets" in request.args:
@@ -114,7 +114,7 @@ def api_diagonalley_indexers():
"zone2cost": {"type": "integer", "min": 0, "required": True},
}
)
-def api_diagonalley_indexer_create(indexer_id=None):
+async def api_diagonalley_indexer_create(indexer_id=None):
if indexer_id:
indexer = get_diagonalleys_indexer(indexer_id)
@@ -134,7 +134,7 @@ def api_diagonalley_indexer_create(indexer_id=None):
@diagonalley_ext.route("/api/v1/diagonalley/indexers/", methods=["DELETE"])
@api_check_wallet_key(key_type="invoice")
-def api_diagonalley_indexer_delete(indexer_id):
+async def api_diagonalley_indexer_delete(indexer_id):
indexer = get_diagonalleys_indexer(indexer_id)
if not indexer:
@@ -153,7 +153,7 @@ def api_diagonalley_indexer_delete(indexer_id):
@diagonalley_ext.route("/api/v1/diagonalley/orders", methods=["GET"])
@api_check_wallet_key(key_type="invoice")
-def api_diagonalley_orders():
+async def api_diagonalley_orders():
wallet_ids = [g.wallet.id]
if "all_wallets" in request.args:
@@ -173,14 +173,14 @@ def api_diagonalley_orders():
"shippingzone": {"type": "integer", "empty": False, "required": True},
}
)
-def api_diagonalley_order_create():
+async def api_diagonalley_order_create():
order = create_diagonalleys_order(wallet_id=g.wallet.id, **g.data)
return jsonify(order._asdict()), HTTPStatus.CREATED
@diagonalley_ext.route("/api/v1/diagonalley/orders/", methods=["DELETE"])
@api_check_wallet_key(key_type="invoice")
-def api_diagonalley_order_delete(order_id):
+async def api_diagonalley_order_delete(order_id):
order = get_diagonalleys_order(order_id)
if not order:
@@ -196,7 +196,7 @@ def api_diagonalley_order_delete(order_id):
@diagonalley_ext.route("/api/v1/diagonalley/orders/paid/", methods=["GET"])
@api_check_wallet_key(key_type="invoice")
-def api_diagonalleys_order_paid(order_id):
+async def api_diagonalleys_order_paid(order_id):
with open_ext_db("diagonalley") as db:
db.execute(
"UPDATE orders SET paid = ? WHERE id = ?",
@@ -210,7 +210,7 @@ def api_diagonalleys_order_paid(order_id):
@diagonalley_ext.route("/api/v1/diagonalley/orders/shipped/", methods=["GET"])
@api_check_wallet_key(key_type="invoice")
-def api_diagonalleys_order_shipped(order_id):
+async def api_diagonalleys_order_shipped(order_id):
with open_ext_db("diagonalley") as db:
db.execute(
"UPDATE orders SET shipped = ? WHERE id = ?",
@@ -228,7 +228,7 @@ def api_diagonalleys_order_shipped(order_id):
@diagonalley_ext.route("/api/v1/diagonalley/stall/products/", methods=["GET"])
-def api_diagonalleys_stall_products(indexer_id):
+async def api_diagonalleys_stall_products(indexer_id):
with open_ext_db("diagonalley") as db:
rows = db.fetchone("SELECT * FROM indexers WHERE id = ?", (indexer_id,))
print(rows[1])
@@ -246,7 +246,7 @@ def api_diagonalleys_stall_products(indexer_id):
@diagonalley_ext.route("/api/v1/diagonalley/stall/checkshipped/", methods=["GET"])
-def api_diagonalleys_stall_checkshipped(checking_id):
+async def api_diagonalleys_stall_checkshipped(checking_id):
with open_ext_db("diagonalley") as db:
rows = db.fetchone("SELECT * FROM orders WHERE invoiceid = ?", (checking_id,))
@@ -266,7 +266,7 @@ def api_diagonalleys_stall_checkshipped(checking_id):
"shippingzone": {"type": "integer", "empty": False, "required": True},
}
)
-def api_diagonalley_stall_order(indexer_id):
+async def api_diagonalley_stall_order(indexer_id):
product = get_diagonalleys_product(g.data["id"])
shipping = get_diagonalleys_indexer(indexer_id)
diff --git a/lnbits/extensions/events/__init__.py b/lnbits/extensions/events/__init__.py
index 52d499ea..abd48951 100644
--- a/lnbits/extensions/events/__init__.py
+++ b/lnbits/extensions/events/__init__.py
@@ -1,4 +1,4 @@
-from flask import Blueprint
+from quart import Blueprint
events_ext: Blueprint = Blueprint("events", __name__, static_folder="static", template_folder="templates")
diff --git a/lnbits/extensions/events/templates/events/display.html b/lnbits/extensions/events/templates/events/display.html
index 542feb91..927c68ff 100644
--- a/lnbits/extensions/events/templates/events/display.html
+++ b/lnbits/extensions/events/templates/events/display.html
@@ -82,7 +82,6 @@
{% endblock %} {% block scripts %}
-
-{% assets filters='rjsmin', output='__bundle__/core/chart.js',
-'vendor/moment@2.27.0/moment.min.js', 'vendor/chart.js@2.9.3/chart.min.js' %}
-
-{% endassets %} {% assets filters='rjsmin', output='__bundle__/core/wallet.js',
-'vendor/bolt11/utils.js', 'vendor/bolt11/decoder.js',
-'vendor/vue-qrcode-reader@2.2.0/vue-qrcode-reader.min.js' %}
-
-{% endassets %}
-
{% endblock %}
diff --git a/lnbits/extensions/lndhub/views.py b/lnbits/extensions/lndhub/views.py
index 81e4c69d..e9478ff1 100644
--- a/lnbits/extensions/lndhub/views.py
+++ b/lnbits/extensions/lndhub/views.py
@@ -1,4 +1,4 @@
-from flask import render_template, g
+from quart import render_template, g
from lnbits.decorators import check_user_exists, validate_uuids
from lnbits.extensions.lndhub import lndhub_ext
@@ -7,5 +7,5 @@ from lnbits.extensions.lndhub import lndhub_ext
@lndhub_ext.route("/")
@validate_uuids(["usr"], required=True)
@check_user_exists()
-def lndhub_index():
- return render_template("lndhub/index.html", user=g.user)
+async def lndhub_index():
+ return await render_template("lndhub/index.html", user=g.user)
diff --git a/lnbits/extensions/lndhub/views_api.py b/lnbits/extensions/lndhub/views_api.py
index 92fc6e5b..c413c461 100644
--- a/lnbits/extensions/lndhub/views_api.py
+++ b/lnbits/extensions/lndhub/views_api.py
@@ -1,6 +1,6 @@
import time
from base64 import urlsafe_b64encode
-from flask import jsonify, g, request
+from quart import jsonify, g, request
from lnbits.core.services import pay_invoice, create_invoice
from lnbits.core.crud import delete_expired_invoices
@@ -14,7 +14,7 @@ from .utils import to_buffer, decoded_as_lndhub
@lndhub_ext.route("/ext/getinfo", methods=["GET"])
-def lndhub_getinfo():
+async def lndhub_getinfo():
return jsonify({"error": True, "code": 1, "message": "bad auth"})
@@ -26,7 +26,7 @@ def lndhub_getinfo():
"refresh_token": {"type": "string", "required": True, "excludes": ["login", "password"]},
}
)
-def lndhub_auth():
+async def lndhub_auth():
token = (
g.data["token"]
if "token" in g.data and g.data["token"]
@@ -44,7 +44,7 @@ def lndhub_auth():
"preimage": {"type": "string", "required": False},
}
)
-def lndhub_addinvoice():
+async def lndhub_addinvoice():
try:
_, pr = create_invoice(
wallet_id=g.wallet.id,
@@ -76,7 +76,7 @@ def lndhub_addinvoice():
@lndhub_ext.route("/ext/payinvoice", methods=["POST"])
@check_wallet(requires_admin=True)
@api_validate_post_request(schema={"invoice": {"type": "string", "required": True}})
-def lndhub_payinvoice():
+async def lndhub_payinvoice():
try:
pay_invoice(
wallet_id=g.wallet.id,
@@ -112,13 +112,13 @@ def lndhub_payinvoice():
@lndhub_ext.route("/ext/balance", methods=["GET"])
@check_wallet()
-def lndhub_balance():
+async def lndhub_balance():
return jsonify({"BTC": {"AvailableBalance": g.wallet.balance}})
@lndhub_ext.route("/ext/gettxs", methods=["GET"])
@check_wallet()
-def lndhub_gettxs():
+async def lndhub_gettxs():
for payment in g.wallet.get_payments(
complete=False, pending=True, outgoing=True, incoming=False, exclude_uncheckable=True
):
@@ -146,7 +146,7 @@ def lndhub_gettxs():
@lndhub_ext.route("/ext/getuserinvoices", methods=["GET"])
@check_wallet()
-def lndhub_getuserinvoices():
+async def lndhub_getuserinvoices():
delete_expired_invoices()
for invoice in g.wallet.get_payments(
complete=False, pending=True, outgoing=False, incoming=True, exclude_uncheckable=True
@@ -177,26 +177,26 @@ def lndhub_getuserinvoices():
@lndhub_ext.route("/ext/getbtc", methods=["GET"])
@check_wallet()
-def lndhub_getbtc():
+async def lndhub_getbtc():
"load an address for incoming onchain btc"
return jsonify([])
@lndhub_ext.route("/ext/getpending", methods=["GET"])
@check_wallet()
-def lndhub_getpending():
+async def lndhub_getpending():
"pending onchain transactions"
return jsonify([])
@lndhub_ext.route("/ext/decodeinvoice", methods=["GET"])
-def lndhub_decodeinvoice():
+async def lndhub_decodeinvoice():
invoice = request.args.get("invoice")
inv = bolt11.decode(invoice)
return jsonify(decoded_as_lndhub(inv))
@lndhub_ext.route("/ext/checkrouteinvoice", methods=["GET"])
-def lndhub_checkrouteinvoice():
+async def lndhub_checkrouteinvoice():
"not implemented on canonical lndhub"
pass
diff --git a/lnbits/extensions/lnticket/__init__.py b/lnbits/extensions/lnticket/__init__.py
index 08ca29cf..7a91b6b6 100644
--- a/lnbits/extensions/lnticket/__init__.py
+++ b/lnbits/extensions/lnticket/__init__.py
@@ -1,4 +1,4 @@
-from flask import Blueprint
+from quart import Blueprint
lnticket_ext: Blueprint = Blueprint("lnticket", __name__, static_folder="static", template_folder="templates")
diff --git a/lnbits/extensions/lnticket/templates/lnticket/display.html b/lnbits/extensions/lnticket/templates/lnticket/display.html
index 4ab829e5..59553ab3 100644
--- a/lnbits/extensions/lnticket/templates/lnticket/display.html
+++ b/lnbits/extensions/lnticket/templates/lnticket/display.html
@@ -76,7 +76,6 @@
{% endblock %} {% block scripts %}
-
-{% assets filters='rjsmin', output='__bundle__/withdraw/index.js',
-'withdraw/js/index.js' %}
-
-{% endassets %} {% endblock %} {% block page %}
+
+{% endblock %} {% block page %}
@@ -273,7 +270,7 @@
color="deep-purple"
:disable="
simpleformDialog.data.wallet == null ||
-
+
simpleformDialog.data.max_withdrawable == null ||
simpleformDialog.data.max_withdrawable < 1 ||
simpleformDialog.data.uses == null"
diff --git a/lnbits/extensions/withdraw/templates/withdraw/print_qr.html b/lnbits/extensions/withdraw/templates/withdraw/print_qr.html
index 06744c35..3a47c6b7 100644
--- a/lnbits/extensions/withdraw/templates/withdraw/print_qr.html
+++ b/lnbits/extensions/withdraw/templates/withdraw/print_qr.html
@@ -1,5 +1,4 @@
-
-{% block page %}
+{% extends "print.html" %} {% block page %}
@@ -50,15 +49,6 @@
}
{% endblock %} {% block scripts %}
-
-
-
-
-
-
-
-
-
- {% else %} {% assets output='__bundle__/vue.js',
- 'vendor/quasar@1.13.2/quasar.ie.polyfills.umd.min.js',
- 'vendor/vue@2.6.12/vue.min.js', 'vendor/vue-router@3.4.3/vue-router.min.js',
- 'vendor/vuex@3.5.1/vuex.min.js', 'vendor/quasar@1.13.2/quasar.umd.min.js' %}
-
- {% endassets %} {% endif %} {% assets filters='rjsmin',
- output='__bundle__/base.js', 'vendor/axios@0.20.0/axios.min.js',
- 'vendor/underscore@1.10.2/underscore.min.js', 'js/base.js',
- 'js/components.js' %}
-
- {% endassets %} {% block scripts %}{% endblock %}
+ {% block vue_templates %}{% endblock %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% block scripts %}{% endblock %}