diff --git a/Pipfile b/Pipfile index 1b816e1a3..24e580c88 100644 --- a/Pipfile +++ b/Pipfile @@ -14,12 +14,8 @@ environs = "*" lnurl = "==0.3.6" pyscss = "*" shortuuid = "*" -quart = "*" -quart-cors = "*" -quart-compress = "*" typing-extensions = "*" httpx = "*" -quart-trio = "*" trio = "==0.16.0" sqlalchemy-aio = "*" embit = "*" @@ -27,6 +23,10 @@ pyqrcode = "*" pypng = "*" sqlalchemy = "==1.3.23" psycopg2-binary = "*" +fastapi = {ref = "anyio", git = "https://github.com/graingert/fastapi"} +trio-asyncio = "*" +hypercorn = {extras = ["trio"], version = "*"} +aiofiles = "*" [dev-packages] black = "==20.8b1" @@ -35,7 +35,3 @@ pytest-cov = "*" mypy = "latest" pytest-trio = "*" trio-typing = "*" - -[packages.hypercorn] -extras = [ "trio",] -version = "*" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 000000000..470cb2bb5 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,810 @@ +{ + "_meta": { + "hash": { + "sha256": "ff9251889371e0cec0eda7eb792e8618ea84f6c7eb85e9b472eacf3d3552c7c4" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.7" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "aiofiles": { + "hashes": [ + "sha256:a1c4fc9b2ff81568c83e21392a82f344ea9d23da906e4f6a52662764545e19d4", + "sha256:c67a6823b5f23fcab0a2595a289cec7d8c863ffcb4322fb8cd6b90400aedfdbc" + ], + "index": "pypi", + "version": "==0.7.0" + }, + "anyio": { + "hashes": [ + "sha256:929a6852074397afe1d989002aa96d457e3e1e5441357c60d03e7eea0e65e1b0", + "sha256:ae57a67583e5ff8b4af47666ff5651c3732d45fd26c929253748e796af860374" + ], + "markers": "python_full_version >= '3.6.2'", + "version": "==3.3.0" + }, + "async-generator": { + "hashes": [ + "sha256:01c7bf666359b4967d2cda0000cc2e4af16a0ae098cbffcb8472fb9e8ad6585b", + "sha256:6ebb3d106c12920aaae42ccb6f787ef5eefdcdd166ea3d628fa8476abe712144" + ], + "markers": "python_version >= '3.5'", + "version": "==1.10" + }, + "attrs": { + "hashes": [ + "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1", + "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==21.2.0" + }, + "bech32": { + "hashes": [ + "sha256:7d6db8214603bd7871fcfa6c0826ef68b85b0abd90fa21c285a9c5e21d2bd899", + "sha256:990dc8e5a5e4feabbdf55207b5315fdd9b73db40be294a19b3752cde9e79d981" + ], + "markers": "python_version >= '3.5'", + "version": "==1.2.0" + }, + "bitstring": { + "hashes": [ + "sha256:0de167daa6a00c9386255a7cac931b45e6e24e0ad7ea64f1f92a64ac23ad4578", + "sha256:a5848a3f63111785224dca8bb4c0a75b62ecdef56a042c8d6be74b16f7e860e7", + "sha256:e3e340e58900a948787a05e8c08772f1ccbe133f6f41fe3f0fa19a18a22bbf4f" + ], + "index": "pypi", + "version": "==3.1.9" + }, + "cerberus": { + "hashes": [ + "sha256:d1b21b3954b2498d9a79edf16b3170a3ac1021df88d197dc2ce5928ba519237c" + ], + "index": "pypi", + "version": "==1.3.4" + }, + "certifi": { + "hashes": [ + "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee", + "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8" + ], + "version": "==2021.5.30" + }, + "charset-normalizer": { + "hashes": [ + "sha256:0c8911edd15d19223366a194a513099a302055a962bca2cec0f54b8b63175d8b", + "sha256:f23667ebe1084be45f6ae0538e4a5a865206544097e4e8bbcacf42cd02a348f3" + ], + "markers": "python_version >= '3.5'", + "version": "==2.0.4" + }, + "ecdsa": { + "hashes": [ + "sha256:5cf31d5b33743abe0dfc28999036c849a69d548f994b535e527ee3cb7f3ef676", + "sha256:b9f500bb439e4153d0330610f5d26baaf18d17b8ced1bc54410d189385ea68aa" + ], + "index": "pypi", + "version": "==0.17.0" + }, + "embit": { + "hashes": [ + "sha256:19f69929caf0d2fcfd4b708dd873384dfc36267944d02d5e6dfebc835f294e1b" + ], + "index": "pypi", + "version": "==0.4.6" + }, + "environs": { + "hashes": [ + "sha256:72b867ff7b553076cdd90f3ee01ecc1cf854987639c9c459f0ed0d3d44ae490c", + "sha256:ee5466156b50fe03aa9fec6e720feea577b5bf515d7f21b2c46608272557ba26" + ], + "index": "pypi", + "version": "==9.3.3" + }, + "fastapi": { + "git": "https://github.com/graingert/fastapi", + "ref": "ada7c747c05c88d37e012d32e97bcc9579f3f006" + }, + "h11": { + "hashes": [ + "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6", + "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042" + ], + "markers": "python_version >= '3.6'", + "version": "==0.12.0" + }, + "h2": { + "hashes": [ + "sha256:ac9e293a1990b339d5d71b19c5fe630e3dd4d768c620d1730d355485323f1b25", + "sha256:bb7ac7099dd67a857ed52c815a6192b6b1f5ba6b516237fc24a085341340593d" + ], + "markers": "python_full_version >= '3.6.1'", + "version": "==4.0.0" + }, + "hpack": { + "hashes": [ + "sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c", + "sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095" + ], + "markers": "python_full_version >= '3.6.1'", + "version": "==4.0.0" + }, + "httpcore": { + "hashes": [ + "sha256:b0d16f0012ec88d8cc848f5a55f8a03158405f4bca02ee49bc4ca2c1fda49f3e", + "sha256:db4c0dcb8323494d01b8c6d812d80091a31e520033e7b0120883d6f52da649ff" + ], + "markers": "python_version >= '3.6'", + "version": "==0.13.6" + }, + "httpx": { + "hashes": [ + "sha256:92ecd2c00c688b529eda11cedb15161eaf02dee9116712f621c70d9a40b2cdd0", + "sha256:9bd728a6c5ec0a9e243932a9983d57d3cc4a87bb4f554e1360fce407f78f9435" + ], + "index": "pypi", + "version": "==0.19.0" + }, + "hypercorn": { + "extras": [ + "trio" + ], + "hashes": [ + "sha256:5ba1e719c521080abd698ff5781a2331e34ef50fc1c89a50960538115a896a9a", + "sha256:8007c10f81566920f8ae12c0e26e146f94ca70506da964b5a727ad610aa1d821" + ], + "index": "pypi", + "version": "==0.11.2" + }, + "hyperframe": { + "hashes": [ + "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15", + "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914" + ], + "markers": "python_full_version >= '3.6.1'", + "version": "==6.0.1" + }, + "idna": { + "hashes": [ + "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a", + "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3" + ], + "version": "==3.2" + }, + "lnurl": { + "hashes": [ + "sha256:579982fd8c4d25bc84c61c74ec45cb7999fa1fa2426f5d5aeb0160ba333b9c92", + "sha256:8af07460115a48f3122a5a9c9a6062bee3897d5f6ab4c9a60f6561a83a8234f6" + ], + "index": "pypi", + "version": "==0.3.6" + }, + "marshmallow": { + "hashes": [ + "sha256:c67929438fd73a2be92128caa0325b1b5ed8b626d91a094d2f7f2771bf1f1c0e", + "sha256:dd4724335d3c2b870b641ffe4a2f8728a1380cd2e7e2312756715ffeaa82b842" + ], + "markers": "python_version >= '3.5'", + "version": "==3.13.0" + }, + "outcome": { + "hashes": [ + "sha256:c7dd9375cfd3c12db9801d080a3b63d4b0a261aa996c4c13152380587288d958", + "sha256:e862f01d4e626e63e8f92c38d1f8d5546d3f9cce989263c521b2e7990d186967" + ], + "markers": "python_version >= '3.6'", + "version": "==1.1.0" + }, + "priority": { + "hashes": [ + "sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa", + "sha256:c965d54f1b8d0d0b19479db3924c7c36cf672dbf2aec92d43fbdaf4492ba18c0" + ], + "markers": "python_full_version >= '3.6.1'", + "version": "==2.0.0" + }, + "psycopg2-binary": { + "hashes": [ + "sha256:0b7dae87f0b729922e06f85f667de7bf16455d411971b2043bbd9577af9d1975", + "sha256:0f2e04bd2a2ab54fa44ee67fe2d002bb90cee1c0f1cc0ebc3148af7b02034cbd", + "sha256:123c3fb684e9abfc47218d3784c7b4c47c8587951ea4dd5bc38b6636ac57f616", + "sha256:1473c0215b0613dd938db54a653f68251a45a78b05f6fc21af4326f40e8360a2", + "sha256:14db1752acdd2187d99cb2ca0a1a6dfe57fc65c3281e0f20e597aac8d2a5bd90", + "sha256:1e3a362790edc0a365385b1ac4cc0acc429a0c0d662d829a50b6ce743ae61b5a", + "sha256:1e85b74cbbb3056e3656f1cc4781294df03383127a8114cbc6531e8b8367bf1e", + "sha256:20f1ab44d8c352074e2d7ca67dc00843067788791be373e67a0911998787ce7d", + "sha256:2f62c207d1740b0bde5c4e949f857b044818f734a3d57f1d0d0edc65050532ed", + "sha256:3242b9619de955ab44581a03a64bdd7d5e470cc4183e8fcadd85ab9d3756ce7a", + "sha256:35c4310f8febe41f442d3c65066ca93cccefd75013df3d8c736c5b93ec288140", + "sha256:4235f9d5ddcab0b8dbd723dca56ea2922b485ea00e1dafacf33b0c7e840b3d32", + "sha256:5ced67f1e34e1a450cdb48eb53ca73b60aa0af21c46b9b35ac3e581cf9f00e31", + "sha256:7360647ea04db2e7dff1648d1da825c8cf68dc5fbd80b8fb5b3ee9f068dcd21a", + "sha256:8c13d72ed6af7fd2c8acbd95661cf9477f94e381fce0792c04981a8283b52917", + "sha256:988b47ac70d204aed01589ed342303da7c4d84b56c2f4c4b8b00deda123372bf", + "sha256:995fc41ebda5a7a663a254a1dcac52638c3e847f48307b5416ee373da15075d7", + "sha256:a36c7eb6152ba5467fb264d73844877be8b0847874d4822b7cf2d3c0cb8cdcb0", + "sha256:aed4a9a7e3221b3e252c39d0bf794c438dc5453bc2963e8befe9d4cd324dff72", + "sha256:aef9aee84ec78af51107181d02fe8773b100b01c5dfde351184ad9223eab3698", + "sha256:b0221ca5a9837e040ebf61f48899926b5783668b7807419e4adae8175a31f773", + "sha256:b4d7679a08fea64573c969f6994a2631908bb2c0e69a7235648642f3d2e39a68", + "sha256:c250a7ec489b652c892e4f0a5d122cc14c3780f9f643e1a326754aedf82d9a76", + "sha256:ca86db5b561b894f9e5f115d6a159fff2a2570a652e07889d8a383b5fae66eb4", + "sha256:cfc523edecddaef56f6740d7de1ce24a2fdf94fd5e704091856a201872e37f9f", + "sha256:da113b70f6ec40e7d81b43d1b139b9db6a05727ab8be1ee559f3a69854a69d34", + "sha256:f6fac64a38f6768e7bc7b035b9e10d8a538a9fadce06b983fb3e6fa55ac5f5ce", + "sha256:f8559617b1fcf59a9aedba2c9838b5b6aa211ffedecabca412b92a1ff75aac1a", + "sha256:fbb42a541b1093385a2d8c7eec94d26d30437d0e77c1d25dae1dcc46741a385e" + ], + "index": "pypi", + "version": "==2.9.1" + }, + "pydantic": { + "hashes": [ + "sha256:021ea0e4133e8c824775a0cfe098677acf6fa5a3cbf9206a376eed3fc09302cd", + "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739", + "sha256:05ef5246a7ffd2ce12a619cbb29f3307b7c4509307b1b49f456657b43529dc6f", + "sha256:10e5622224245941efc193ad1d159887872776df7a8fd592ed746aa25d071840", + "sha256:18b5ea242dd3e62dbf89b2b0ec9ba6c7b5abaf6af85b95a97b00279f65845a23", + "sha256:234a6c19f1c14e25e362cb05c68afb7f183eb931dd3cd4605eafff055ebbf287", + "sha256:244ad78eeb388a43b0c927e74d3af78008e944074b7d0f4f696ddd5b2af43c62", + "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b", + "sha256:41b542c0b3c42dc17da70554bc6f38cbc30d7066d2c2815a94499b5684582ecb", + "sha256:4a03cbbe743e9c7247ceae6f0d8898f7a64bb65800a45cbdc52d65e370570820", + "sha256:4be75bebf676a5f0f87937c6ddb061fa39cbea067240d98e298508c1bda6f3f3", + "sha256:54cd5121383f4a461ff7644c7ca20c0419d58052db70d8791eacbbe31528916b", + "sha256:589eb6cd6361e8ac341db97602eb7f354551482368a37f4fd086c0733548308e", + "sha256:8621559dcf5afacf0069ed194278f35c255dc1a1385c28b32dd6c110fd6531b3", + "sha256:8b223557f9510cf0bfd8b01316bf6dd281cf41826607eada99662f5e4963f316", + "sha256:99a9fc39470010c45c161a1dc584997f1feb13f689ecf645f59bb4ba623e586b", + "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4", + "sha256:a83db7205f60c6a86f2c44a61791d993dff4b73135df1973ecd9eed5ea0bda20", + "sha256:ac8eed4ca3bd3aadc58a13c2aa93cd8a884bcf21cb019f8cfecaae3b6ce3746e", + "sha256:e710876437bc07bd414ff453ac8ec63d219e7690128d925c6e82889d674bb505", + "sha256:ea5cb40a3b23b3265f6325727ddfc45141b08ed665458be8c6285e7b85bd73a1", + "sha256:fec866a0b59f372b7e776f2d7308511784dace622e0992a0b59ea3ccee0ae833" + ], + "markers": "python_full_version >= '3.6.1'", + "version": "==1.8.2" + }, + "pypng": { + "hashes": [ + "sha256:76f8a1539ec56451da7ab7121f12a361969fe0f2d48d703d198ce2a99d6c5afd" + ], + "index": "pypi", + "version": "==0.0.21" + }, + "pyqrcode": { + "hashes": [ + "sha256:1b2812775fa6ff5c527977c4cd2ccb07051ca7d0bc0aecf937a43864abe5eff6", + "sha256:fdbf7634733e56b72e27f9bce46e4550b75a3a2c420414035cae9d9d26b234d5" + ], + "index": "pypi", + "version": "==1.2.1" + }, + "pyscss": { + "hashes": [ + "sha256:f1df571569021a23941a538eb154405dde80bed35dc1ea7c5f3e18e0144746bf" + ], + "index": "pypi", + "version": "==1.3.7" + }, + "python-dotenv": { + "hashes": [ + "sha256:aae25dc1ebe97c420f50b81fb0e5c949659af713f31fdb63c749ca68748f34b1", + "sha256:f521bc2ac9a8e03c736f62911605c5d83970021e3fa95b37d769e2bbbe9b6172" + ], + "markers": "python_version >= '3.5'", + "version": "==0.19.0" + }, + "represent": { + "hashes": [ + "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" + ], + "version": "==1.5.0" + }, + "shortuuid": { + "hashes": [ + "sha256:3c11d2007b915c43bee3e10625f068d8a349e04f0d81f08f5fa08507427ebf1f", + "sha256:492c7402ff91beb1342a5898bd61ea953985bf24a41cd9f247409aa2e03c8f77" + ], + "index": "pypi", + "version": "==1.0.1" + }, + "six": { + "hashes": [ + "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.16.0" + }, + "sniffio": { + "hashes": [ + "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663", + "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de" + ], + "markers": "python_version >= '3.5'", + "version": "==1.2.0" + }, + "sortedcontainers": { + "hashes": [ + "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", + "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0" + ], + "version": "==2.4.0" + }, + "sqlalchemy": { + "hashes": [ + "sha256:040bdfc1d76a9074717a3f43455685f781c581f94472b010cd6c4754754e1862", + "sha256:1fe5d8d39118c2b018c215c37b73fd6893c3e1d4895be745ca8ff6eb83333ed3", + "sha256:23927c3981d1ec6b4ea71eb99d28424b874d9c696a21e5fbd9fa322718be3708", + "sha256:24f9569e82a009a09ce2d263559acb3466eba2617203170e4a0af91e75b4f075", + "sha256:2578dbdbe4dbb0e5126fb37ffcd9793a25dcad769a95f171a2161030bea850ff", + "sha256:269990b3ab53cb035d662dcde51df0943c1417bdab707dc4a7e4114a710504b4", + "sha256:29cccc9606750fe10c5d0e8bd847f17a97f3850b8682aef1f56f5d5e1a5a64b1", + "sha256:37b83bf81b4b85dda273aaaed5f35ea20ad80606f672d94d2218afc565fb0173", + "sha256:63677d0c08524af4c5893c18dbe42141de7178001360b3de0b86217502ed3601", + "sha256:639940bbe1108ac667dcffc79925db2966826c270112e9159439ab6bb14f8d80", + "sha256:6a939a868fdaa4b504e8b9d4a61f21aac11e3fecc8a8214455e144939e3d2aea", + "sha256:6b8b8c80c7f384f06825612dd078e4a31f0185e8f1f6b8c19e188ff246334205", + "sha256:6c9e6cc9237de5660bcddea63f332428bb83c8e2015c26777281f7ffbd2efb84", + "sha256:6ec1044908414013ebfe363450c22f14698803ce97fbb47e53284d55c5165848", + "sha256:6fca33672578666f657c131552c4ef8979c1606e494f78cd5199742dfb26918b", + "sha256:751934967f5336a3e26fc5993ccad1e4fee982029f9317eb6153bc0bc3d2d2da", + "sha256:8be835aac18ec85351385e17b8665bd4d63083a7160a017bef3d640e8e65cadb", + "sha256:927ce09e49bff3104459e1451ce82983b0a3062437a07d883a4c66f0b344c9b5", + "sha256:94208867f34e60f54a33a37f1c117251be91a47e3bfdb9ab8a7847f20886ad06", + "sha256:94f667d86be82dd4cb17d08de0c3622e77ca865320e0b95eae6153faa7b4ecaf", + "sha256:9e9c25522933e569e8b53ccc644dc993cab87e922fb7e142894653880fdd419d", + "sha256:a0e306e9bb76fd93b29ae3a5155298e4c1b504c7cbc620c09c20858d32d16234", + "sha256:a8bfc1e1afe523e94974132d7230b82ca7fa2511aedde1f537ec54db0399541a", + "sha256:ac2244e64485c3778f012951fdc869969a736cd61375fde6096d08850d8be729", + "sha256:b4b0e44d586cd64b65b507fa116a3814a1a53d55dce4836d7c1a6eb2823ff8d1", + "sha256:baeb451ee23e264de3f577fee5283c73d9bbaa8cb921d0305c0bbf700094b65b", + "sha256:c7dc052432cd5d060d7437e217dd33c97025287f99a69a50e2dc1478dd610d64", + "sha256:d1a85dfc5dee741bf49cb9b6b6b8d2725a268e4992507cf151cba26b17d97c37", + "sha256:d90010304abb4102123d10cbad2cdf2c25a9f2e66a50974199b24b468509bad5", + "sha256:ddfb511e76d016c3a160910642d57f4587dc542ce5ee823b0d415134790eeeb9", + "sha256:e273367f4076bd7b9a8dc2e771978ef2bfd6b82526e80775a7db52bff8ca01dd", + "sha256:e5bb3463df697279e5459a7316ad5a60b04b0107f9392e88674d0ece70e9cf70", + "sha256:e8a1750b44ad6422ace82bf3466638f1aa0862dbb9689690d5f2f48cce3476c8", + "sha256:eab063a70cca4a587c28824e18be41d8ecc4457f8f15b2933584c6c6cccd30f0", + "sha256:ecce8c021894a77d89808222b1ff9687ad84db54d18e4bd0500ca766737faaf6", + "sha256:f4d972139d5000105fcda9539a76452039434013570d6059993120dc2a65e447", + "sha256:fd3b96f8c705af8e938eaa99cbd8fd1450f632d38cad55e7367c33b263bf98ec", + "sha256:fdd2ed7395df8ac2dbb10cefc44737b66c6a5cd7755c92524733d7a443e5b7e2" + ], + "index": "pypi", + "version": "==1.3.23" + }, + "sqlalchemy-aio": { + "hashes": [ + "sha256:7f77366f55d34891c87386dd0962a28b948b684e8ea5edb7daae4187c0b291bf", + "sha256:f767320427c22c66fa5840a1f17f3261110a8ddc8560558f4fbf12d31a66b17b" + ], + "index": "pypi", + "version": "==0.16.0" + }, + "toml": { + "hashes": [ + "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", + "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==0.10.2" + }, + "trio": { + "hashes": [ + "sha256:451ddb27b4e5215e00646fcbb8028d341fccf284e053dc376506a14bb133dbcf", + "sha256:df067dd0560c321af39d412cd81fc3a7d13f55af9150527daab980683e9fcf3c" + ], + "index": "pypi", + "version": "==0.16.0" + }, + "trio-asyncio": { + "hashes": [ + "sha256:824be23b0c678c0df942816cdb57b92a8b94f264fffa89f04626b0ba2d009768", + "sha256:9bf678f83204ba33c395783681c69af563a84145fad2110a152a81a4a18ae7e4" + ], + "index": "pypi", + "version": "==0.12.0" + }, + "typing-extensions": { + "hashes": [ + "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497", + "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342", + "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84" + ], + "index": "pypi", + "version": "==3.10.0.0" + }, + "wsproto": { + "hashes": [ + "sha256:868776f8456997ad0d9720f7322b746bbe9193751b5b290b7f924659377c8c38", + "sha256:d8345d1808dd599b5ffb352c25a367adb6157e664e140dbecba3f9bc007edb9f" + ], + "markers": "python_full_version >= '3.6.1'", + "version": "==1.0.0" + } + }, + "develop": { + "appdirs": { + "hashes": [ + "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", + "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128" + ], + "version": "==1.4.4" + }, + "async-generator": { + "hashes": [ + "sha256:01c7bf666359b4967d2cda0000cc2e4af16a0ae098cbffcb8472fb9e8ad6585b", + "sha256:6ebb3d106c12920aaae42ccb6f787ef5eefdcdd166ea3d628fa8476abe712144" + ], + "markers": "python_version >= '3.5'", + "version": "==1.10" + }, + "attrs": { + "hashes": [ + "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1", + "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==21.2.0" + }, + "black": { + "hashes": [ + "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea" + ], + "index": "pypi", + "version": "==20.8b1" + }, + "click": { + "hashes": [ + "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a", + "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6" + ], + "markers": "python_version >= '3.6'", + "version": "==8.0.1" + }, + "coverage": { + "hashes": [ + "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c", + "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6", + "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45", + "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a", + "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03", + "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529", + "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a", + "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a", + "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2", + "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6", + "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759", + "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53", + "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a", + "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4", + "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff", + "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502", + "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793", + "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb", + "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905", + "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821", + "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b", + "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81", + "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0", + "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b", + "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3", + "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184", + "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701", + "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a", + "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82", + "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638", + "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5", + "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083", + "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6", + "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90", + "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465", + "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a", + "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3", + "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e", + "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066", + "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf", + "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b", + "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae", + "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669", + "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873", + "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b", + "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6", + "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb", + "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160", + "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c", + "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079", + "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d", + "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6" + ], + "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.5" + }, + "idna": { + "hashes": [ + "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a", + "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3" + ], + "version": "==3.2" + }, + "importlib-metadata": { + "hashes": [ + "sha256:9e04bf59076a15a9b6dd9c27806e8fcdf15280ba529c6a8cc3f4d5b4875bdd61", + "sha256:c4eb3dec5f697682e383a39701a7de11cd5c02daf8dd93534b69e3e6473f6b1b" + ], + "markers": "python_version < '3.8'", + "version": "==4.7.1" + }, + "iniconfig": { + "hashes": [ + "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3", + "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32" + ], + "version": "==1.1.1" + }, + "mypy": { + "hashes": [ + "sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9", + "sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a", + "sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9", + "sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e", + "sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2", + "sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212", + "sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b", + "sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885", + "sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150", + "sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703", + "sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072", + "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457", + "sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e", + "sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0", + "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb", + "sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97", + "sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8", + "sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811", + "sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6", + "sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de", + "sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504", + "sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921", + "sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d" + ], + "index": "pypi", + "version": "==0.910" + }, + "mypy-extensions": { + "hashes": [ + "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", + "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8" + ], + "version": "==0.4.3" + }, + "outcome": { + "hashes": [ + "sha256:c7dd9375cfd3c12db9801d080a3b63d4b0a261aa996c4c13152380587288d958", + "sha256:e862f01d4e626e63e8f92c38d1f8d5546d3f9cce989263c521b2e7990d186967" + ], + "markers": "python_version >= '3.6'", + "version": "==1.1.0" + }, + "packaging": { + "hashes": [ + "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7", + "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14" + ], + "markers": "python_version >= '3.6'", + "version": "==21.0" + }, + "pathspec": { + "hashes": [ + "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a", + "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1" + ], + "version": "==0.9.0" + }, + "pluggy": { + "hashes": [ + "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", + "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==0.13.1" + }, + "py": { + "hashes": [ + "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3", + "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.10.0" + }, + "pyparsing": { + "hashes": [ + "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", + "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" + ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.4.7" + }, + "pytest": { + "hashes": [ + "sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b", + "sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890" + ], + "index": "pypi", + "version": "==6.2.4" + }, + "pytest-cov": { + "hashes": [ + "sha256:261bb9e47e65bd099c89c3edf92972865210c36813f80ede5277dceb77a4a62a", + "sha256:261ceeb8c227b726249b376b8526b600f38667ee314f910353fa318caa01f4d7" + ], + "index": "pypi", + "version": "==2.12.1" + }, + "pytest-trio": { + "hashes": [ + "sha256:c01b741819aec2c419555f28944e132d3c711dae1e673d63260809bf92c30c31" + ], + "index": "pypi", + "version": "==0.7.0" + }, + "regex": { + "hashes": [ + "sha256:08d74bfaa4c7731b8dac0a992c63673a2782758f7cfad34cf9c1b9184f911354", + "sha256:0fc1f8f06977c2d4f5e3d3f0d4a08089be783973fc6b6e278bde01f0544ff308", + "sha256:121f4b3185feaade3f85f70294aef3f777199e9b5c0c0245c774ae884b110a2d", + "sha256:1413b5022ed6ac0d504ba425ef02549a57d0f4276de58e3ab7e82437892704fc", + "sha256:1743345e30917e8c574f273f51679c294effba6ad372db1967852f12c76759d8", + "sha256:28fc475f560d8f67cc8767b94db4c9440210f6958495aeae70fac8faec631797", + "sha256:31a99a4796bf5aefc8351e98507b09e1b09115574f7c9dbb9cf2111f7220d2e2", + "sha256:328a1fad67445550b982caa2a2a850da5989fd6595e858f02d04636e7f8b0b13", + "sha256:473858730ef6d6ff7f7d5f19452184cd0caa062a20047f6d6f3e135a4648865d", + "sha256:4cde065ab33bcaab774d84096fae266d9301d1a2f5519d7bd58fc55274afbf7a", + "sha256:5f6a808044faae658f546dd5f525e921de9fa409de7a5570865467f03a626fc0", + "sha256:610b690b406653c84b7cb6091facb3033500ee81089867ee7d59e675f9ca2b73", + "sha256:66256b6391c057305e5ae9209941ef63c33a476b73772ca967d4a2df70520ec1", + "sha256:6eebf512aa90751d5ef6a7c2ac9d60113f32e86e5687326a50d7686e309f66ed", + "sha256:79aef6b5cd41feff359acaf98e040844613ff5298d0d19c455b3d9ae0bc8c35a", + "sha256:808ee5834e06f57978da3e003ad9d6292de69d2bf6263662a1a8ae30788e080b", + "sha256:8e44769068d33e0ea6ccdf4b84d80c5afffe5207aa4d1881a629cf0ef3ec398f", + "sha256:999ad08220467b6ad4bd3dd34e65329dd5d0df9b31e47106105e407954965256", + "sha256:9b006628fe43aa69259ec04ca258d88ed19b64791693df59c422b607b6ece8bb", + "sha256:a577a21de2ef8059b58f79ff76a4da81c45a75fe0bfb09bc8b7bb4293fa18983", + "sha256:a617593aeacc7a691cc4af4a4410031654f2909053bd8c8e7db837f179a630eb", + "sha256:abb48494d88e8a82601af905143e0de838c776c1241d92021e9256d5515b3645", + "sha256:ac88856a8cbccfc14f1b2d0b829af354cc1743cb375e7f04251ae73b2af6adf8", + "sha256:b844fb09bd9936ed158ff9df0ab601e2045b316b17aa8b931857365ea8586906", + "sha256:bdc178caebd0f338d57ae445ef8e9b737ddf8fbc3ea187603f65aec5b041248f", + "sha256:c61dcc1cf9fd165127a2853e2c31eb4fb961a4f26b394ac9fe5669c7a6592892", + "sha256:c7cb4c512d2d3b0870e00fbbac2f291d4b4bf2634d59a31176a87afe2777c6f0", + "sha256:d4a332404baa6665b54e5d283b4262f41f2103c255897084ec8f5487ce7b9e8e", + "sha256:d5111d4c843d80202e62b4fdbb4920db1dcee4f9366d6b03294f45ed7b18b42e", + "sha256:e1e8406b895aba6caa63d9fd1b6b1700d7e4825f78ccb1e5260551d168db38ed", + "sha256:e8690ed94481f219a7a967c118abaf71ccc440f69acd583cab721b90eeedb77c", + "sha256:ed283ab3a01d8b53de3a05bfdf4473ae24e43caee7dcb5584e86f3f3e5ab4374", + "sha256:ee329d0387b5b41a5dddbb6243a21cb7896587a651bebb957e2d2bb8b63c0791", + "sha256:f3bf1bc02bc421047bfec3343729c4bbbea42605bcfd6d6bfe2c07ade8b12d2a", + "sha256:f585cbbeecb35f35609edccb95efd95a3e35824cd7752b586503f7e6087303f1", + "sha256:f60667673ff9c249709160529ab39667d1ae9fd38634e006bec95611f632e759" + ], + "version": "==2021.8.28" + }, + "sniffio": { + "hashes": [ + "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663", + "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de" + ], + "markers": "python_version >= '3.5'", + "version": "==1.2.0" + }, + "sortedcontainers": { + "hashes": [ + "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", + "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0" + ], + "version": "==2.4.0" + }, + "toml": { + "hashes": [ + "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", + "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==0.10.2" + }, + "trio": { + "hashes": [ + "sha256:451ddb27b4e5215e00646fcbb8028d341fccf284e053dc376506a14bb133dbcf", + "sha256:df067dd0560c321af39d412cd81fc3a7d13f55af9150527daab980683e9fcf3c" + ], + "index": "pypi", + "version": "==0.16.0" + }, + "trio-typing": { + "hashes": [ + "sha256:3eae317514ca18af158bd14ec55ccf20e8b1461efc3a431b87c337a9ca97180b", + "sha256:c3717f097eab29f8deb58a6976da366bd98adb81d90f38002b564932839eaa84" + ], + "index": "pypi", + "version": "==0.5.1" + }, + "typed-ast": { + "hashes": [ + "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace", + "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff", + "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266", + "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528", + "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6", + "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808", + "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4", + "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363", + "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341", + "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04", + "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41", + "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e", + "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3", + "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899", + "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805", + "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c", + "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c", + "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39", + "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a", + "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3", + "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7", + "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f", + "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075", + "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0", + "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40", + "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428", + "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927", + "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3", + "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f", + "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65" + ], + "markers": "python_version < '3.8'", + "version": "==1.4.3" + }, + "typing-extensions": { + "hashes": [ + "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497", + "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342", + "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84" + ], + "index": "pypi", + "version": "==3.10.0.0" + }, + "zipp": { + "hashes": [ + "sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3", + "sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4" + ], + "markers": "python_version >= '3.6'", + "version": "==3.5.0" + } + } +} diff --git a/lnbits/app.py b/lnbits/app.py index 716979fdc..177785bf4 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -1,37 +1,27 @@ -import jinja2 -from lnbits.jinja2_templating import Jinja2Templates -import sys -import warnings import importlib +import sys import traceback -import trio +import warnings +import trio from fastapi import FastAPI, Request +from fastapi.exceptions import RequestValidationError from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.gzip import GZipMiddleware from fastapi.staticfiles import StaticFiles +import lnbits.settings + from .commands import db_migrate, handle_assets from .core import core_app -from .helpers import ( - get_valid_extensions, - get_js_vendored, - get_css_vendored, - url_for_vendored, -) -from .proxy_fix import ASGIProxyFix -from .tasks import ( - webhook_handler, - invoice_listener, - run_deferred_async, - check_pending_payments, - internal_invoice_listener, - catch_everything_and_restart, -) -from .settings import WALLET -from .requestvars import g, request_global from .core.views.generic import core_html_routes -import lnbits.settings +from .helpers import (get_css_vendored, get_js_vendored, get_valid_extensions, + template_renderer, url_for_vendored) +from .requestvars import g +from .settings import WALLET +from .tasks import (check_pending_payments, internal_invoice_listener, + invoice_listener, run_deferred_async, webhook_handler) + async def create_app(config_object="lnbits.settings") -> FastAPI: """Create application factory. @@ -55,7 +45,16 @@ async def create_app(config_object="lnbits.settings") -> FastAPI: ) g().config = lnbits.settings - g().templates = build_standard_jinja_templates() + g().base_url = f"http://{lnbits.settings.HOST}:{lnbits.settings.PORT}" + + @app.exception_handler(RequestValidationError) + async def validation_exception_handler(request: Request, exc: RequestValidationError): + return template_renderer().TemplateResponse("error.html", {"request": request, "err": f"`{exc.errors()}` is not a valid UUID."}) + + # return HTMLResponse( + # status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + # content=jsonable_encoder({"detail": exc.errors(), "body": exc.body}), + # ) app.add_middleware(GZipMiddleware, minimum_size=1000) # app.add_middleware(ASGIProxyFix) @@ -69,26 +68,6 @@ async def create_app(config_object="lnbits.settings") -> FastAPI: return app -def build_standard_jinja_templates(): - t = Jinja2Templates( - loader=jinja2.FileSystemLoader(["lnbits/templates", "lnbits/core/templates"]), - ) - t.env.globals["SITE_TITLE"] = lnbits.settings.LNBITS_SITE_TITLE - t.env.globals["SITE_TAGLINE"] = lnbits.settings.LNBITS_SITE_TAGLINE - t.env.globals["SITE_DESCRIPTION"] = lnbits.settings.LNBITS_SITE_DESCRIPTION - t.env.globals["LNBITS_THEME_OPTIONS"] = lnbits.settings.LNBITS_THEME_OPTIONS - t.env.globals["LNBITS_VERSION"] = lnbits.settings.LNBITS_COMMIT - t.env.globals["EXTENSIONS"] = get_valid_extensions() - - if g().config.DEBUG: - t.env.globals["VENDORED_JS"] = map(url_for_vendored, get_js_vendored()) - t.env.globals["VENDORED_CSS"] = map(url_for_vendored, get_css_vendored()) - else: - t.env.globals["VENDORED_JS"] = ["/static/bundle.js"] - t.env.globals["VENDORED_CSS"] = ["/static/bundle.css"] - - return t - def check_funding_source(app: FastAPI) -> None: @app.on_event("startup") async def check_wallet_status(): @@ -171,5 +150,5 @@ def register_exception_handlers(app): etype, value, tb = sys.exc_info() traceback.print_exception(etype, err, tb) exc = traceback.format_exc() - return g().templates.TemplateResponse("error.html", {"request": request, "err": err}) + return template_renderer().TemplateResponse("error.html", {"request": request, "err": err}) diff --git a/lnbits/core/models.py b/lnbits/core/models.py index c1dc99f91..d7d211bfd 100644 --- a/lnbits/core/models.py +++ b/lnbits/core/models.py @@ -1,7 +1,7 @@ import json import hmac import hashlib -from quart import url_for +from lnbits.helpers import url_for from ecdsa import SECP256k1, SigningKey # type: ignore from lnurl import encode as lnurl_encode # type: ignore from typing import List, NamedTuple, Optional, Dict @@ -30,11 +30,12 @@ class Wallet(BaseModel): @property def lnurlwithdraw_full(self) -> str: + url = url_for( - "core.lnurl_full_withdraw", + "/withdraw", + external=True, usr=self.user, wal=self.id, - _external=True, ) try: return lnurl_encode(url) diff --git a/lnbits/core/services.py b/lnbits/core/services.py index 09b9f4f71..4fa75f9d9 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -5,7 +5,6 @@ from io import BytesIO from binascii import unhexlify from typing import Optional, Tuple, Dict from urllib.parse import urlparse, parse_qs -from quart import g, url_for from lnurl import LnurlErrorResponse, decode as decode_lnurl # type: ignore try: @@ -15,9 +14,10 @@ except ImportError: # pragma: nocover from lnbits import bolt11 from lnbits.db import Connection -from lnbits.helpers import urlsafe_short_hash +from lnbits.helpers import url_for, urlsafe_short_hash from lnbits.settings import WALLET from lnbits.wallets.base import PaymentStatus, PaymentResponse +from lnbits.requestvars import g from . import db from .crud import ( @@ -220,10 +220,9 @@ async def redeem_lnurl_withdraw( try: params["balanceNotify"] = url_for( - "core.lnurl_balance_notify", - service=urlparse(lnurl_request).netloc, + f"/withdraw/notify/{urlparse(lnurl_request).netloc}", + external=True, wal=wallet_id, - _external=True, ) except Exception: pass @@ -242,7 +241,7 @@ async def perform_lnurlauth( cb = urlparse(callback) k1 = unhexlify(parse_qs(cb.query)["k1"][0]) - key = g.wallet.lnurlauth_key(cb.netloc) + key = g().wallet.lnurlauth_key(cb.netloc) def int_to_bytes_suitable_der(x: int) -> bytes: """for strict DER we need to encode the integer with some quirks""" diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 44f168022..7f6604785 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -1,3 +1,4 @@ +from lnbits.helpers import url_for from fastapi.param_functions import Depends from lnbits.auth_bearer import AuthBearer from pydantic import BaseModel @@ -6,7 +7,6 @@ import json import httpx import hashlib from urllib.parse import urlparse, urlunparse, urlencode, parse_qs, ParseResult -from quart import current_app, make_response, url_for from fastapi import Query @@ -121,10 +121,9 @@ async def api_payments_create_invoice(data: CreateInvoiceData): params={ "pr": payment_request, "balanceNotify": url_for( - "core.lnurl_balance_notify", - service=urlparse(data.lnurl_callback).netloc, + f"/withdraw/notify/{urlparse(data.lnurl_callback).netloc}", + external=True, wal=g().wallet.id, - _external=True, ), }, timeout=10, diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index 516bcd5c2..c5d6b3f24 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -1,32 +1,24 @@ -from lnbits.core.models import Wallet -from fastapi.params import Query -from fastapi.routing import APIRouter -from fastapi.responses import RedirectResponse -from fastapi import status -from lnbits.requestvars import g -from os import path from http import HTTPStatus from typing import Optional -import jinja2 +from fastapi import Request, status +from fastapi.param_functions import Body +from fastapi.params import Depends, Query +from fastapi.responses import FileResponse, RedirectResponse +from fastapi.routing import APIRouter +from pydantic.types import UUID4 from starlette.responses import HTMLResponse -from lnbits.core import core_app, db -from lnbits.decorators import check_user_exists, validate_uuids -from lnbits.settings import LNBITS_ALLOWED_USERS, SERVICE_FEE, LNBITS_SITE_TITLE +from lnbits.core import db +from lnbits.helpers import template_renderer, url_for +from lnbits.requestvars import g +from lnbits.settings import (LNBITS_ALLOWED_USERS, LNBITS_SITE_TITLE, + SERVICE_FEE) -from ..crud import ( - create_account, - get_user, - update_user_extension, - create_wallet, - delete_wallet, - get_balance_check, - save_balance_notify, -) -from ..services import redeem_lnurl_withdraw, pay_invoice -from fastapi import FastAPI, Request -from fastapi.responses import FileResponse +from ..crud import (create_account, create_wallet, delete_wallet, + get_balance_check, get_user, save_balance_notify, + update_user_extension) +from ..services import pay_invoice, redeem_lnurl_withdraw core_html_routes: APIRouter = APIRouter(tags=["Core NON-API Website Routes"]) @@ -37,13 +29,13 @@ async def favicon(): @core_html_routes.get("/", response_class=HTMLResponse) async def home(request: Request, lightning: str = None): - return g().templates.TemplateResponse("core/index.html", {"request": request, "lnurl": lightning}) + return template_renderer().TemplateResponse("core/index.html", {"request": request, "lnurl": lightning}) @core_html_routes.get("/extensions") -@validate_uuids(["usr"], required=True) -@check_user_exists() -async def extensions(enable: str, disable: str): +# @validate_uuids(["usr"], required=True) +# @check_user_exists() +async def extensions(request: Request, enable: str, disable: str): extension_to_enable = enable extension_to_disable = disable @@ -60,20 +52,16 @@ async def extensions(enable: str, disable: str): await update_user_extension( user_id=g.user.id, extension=extension_to_disable, active=False ) - return await templates.TemplateResponse("core/extensions.html", {"request": request, "user": get_user(g.user.id)}) + return template_renderer().TemplateResponse("core/extensions.html", {"request": request, "user": get_user(g.user.id)}) @core_html_routes.get("/wallet", response_class=HTMLResponse) #Not sure how to validate -@validate_uuids(["usr", "wal"]) -async def wallet(request: Request, - usr: Optional[str] = Query(None), - wal: Optional[str] = Query(None), - nme: Optional[str] = Query(None), - ): - - user_id = usr - wallet_id = wal +# @validate_uuids(["usr", "nme"]) +async def wallet(request: Request = Query(None), nme: Optional[str] = Query(None), + usr: Optional[UUID4] = Query(None), wal: Optional[UUID4] = Query(None)): + user_id = usr.hex if usr else None + wallet_id = wal.hex if wal else None wallet_name = nme service_fee = int(SERVICE_FEE) if int(SERVICE_FEE) == SERVICE_FEE else SERVICE_FEE @@ -84,33 +72,33 @@ async def wallet(request: Request, # nothing: create everything if not user_id: - usr = await get_user((await create_account()).id) + user = await get_user((await create_account()).id) else: - usr = await get_user(user_id) - if not usr: - return g().templates.TemplateResponse("error.html", {"request": request, "err": "User does not exist."}) + user = await get_user(user_id) + if not user: + return template_renderer().TemplateResponse("error.html", {"request": request, "err": "User does not exist."}) if LNBITS_ALLOWED_USERS and user_id not in LNBITS_ALLOWED_USERS: - return g().templates.TemplateResponse("error.html", {"request": request, "err": "User not authorized."}) + return template_renderer().TemplateResponse("error.html", {"request": request, "err": "User not authorized."}) if not wallet_id: - if usr.wallets and not wallet_name: - wal = usr.wallets[0] + if user.wallets and not wallet_name: + wallet = user.wallets[0] else: - wal = await create_wallet(user_id=usr.id, wallet_name=wallet_name) + wallet = await create_wallet(user_id=user.id, wallet_name=wallet_name) - return RedirectResponse(f"/wallet?usr={usr.id}&wal={wal.id}", status_code=status.HTTP_307_TEMPORARY_REDIRECT) + return RedirectResponse(f"/wallet?usr={user.id}&wal={wallet.id}", status_code=status.HTTP_307_TEMPORARY_REDIRECT) - wal = usr.get_wallet(wallet_id) - if not wal: - return g().templates.TemplateResponse("error.html", {"request": request, ...}) + wallet = user.get_wallet(wallet_id) + if not wallet: + return template_renderer().TemplateResponse("error.html", {"request": request, "err": "Wallet not found"}) - return g().templates.TemplateResponse( - "core/wallet.html", {"request":request,"user":usr, "wallet":wal, "service_fee":service_fee} + return template_renderer().TemplateResponse( + "core/wallet.html", {"request":request,"user":user.dict(), "wallet":wallet.dict(), "service_fee":service_fee} ) @core_html_routes.get("/withdraw") -@validate_uuids(["usr", "wal"], required=True) -async def lnurl_full_withdraw(): +# @validate_uuids(["usr", "wal"], required=True) +async def lnurl_full_withdraw(request: Request): user = await get_user(request.args.get("usr")) if not user: return {"status": "ERROR", "reason": "User does not exist."} @@ -122,24 +110,22 @@ async def lnurl_full_withdraw(): return { "tag": "withdrawRequest", "callback": url_for( - "core.lnurl_full_withdraw_callback", + "/withdraw/cb", + external=True, usr=user.id, wal=wallet.id, - _external=True, ), "k1": "0", "minWithdrawable": 1000 if wallet.withdrawable_balance else 0, "maxWithdrawable": wallet.withdrawable_balance, "defaultDescription": f"{LNBITS_SITE_TITLE} balance withdraw from {wallet.id[0:5]}", - "balanceCheck": url_for( - "core.lnurl_full_withdraw", usr=user.id, wal=wallet.id, _external=True - ), + "balanceCheck": url_for("/withdraw", external=True, usr=user.id, wal=wallet.id), } @core_html_routes.get("/withdraw/cb") -@validate_uuids(["usr", "wal"], required=True) -async def lnurl_full_withdraw_callback(): +# @validate_uuids(["usr", "wal"], required=True) +async def lnurl_full_withdraw_callback(request: Request): user = await get_user(request.args.get("usr")) if not user: return {"status": "ERROR", "reason": "User does not exist."} @@ -166,34 +152,35 @@ async def lnurl_full_withdraw_callback(): @core_html_routes.get("/deletewallet") -@validate_uuids(["usr", "wal"], required=True) -@check_user_exists() -async def deletewallet(): - wallet_id = request.args.get("wal", type=str) - user_wallet_ids = g.user.wallet_ids +# @validate_uuids(["usr", "wal"], required=True) +# @check_user_exists() +async def deletewallet(request: Request): + wallet_id = request.path_params.get("wal", type=str) + user_wallet_ids = g().user.wallet_ids if wallet_id not in user_wallet_ids: abort(HTTPStatus.FORBIDDEN, "Not your wallet.") else: - await delete_wallet(user_id=g.user.id, wallet_id=wallet_id) + await delete_wallet(user_id=g().user.id, wallet_id=wallet_id) user_wallet_ids.remove(wallet_id) if user_wallet_ids: - return redirect(url_for("core.wallet", usr=g.user.id, wal=user_wallet_ids[0])) + return RedirectResponse(url_for("/wallet", usr=g().user.id, wal=user_wallet_ids[0]), + status_code=status.HTTP_307_TEMPORARY_REDIRECT) - return redirect(url_for("core.home")) + return RedirectResponse(url_for("/"), status_code=status.HTTP_307_TEMPORARY_REDIRECT) @core_html_routes.get("/withdraw/notify/{service}") -@validate_uuids(["wal"], required=True) -async def lnurl_balance_notify(service: str): +# @validate_uuids(["wal"], required=True) +async def lnurl_balance_notify(request: Request, service: str): bc = await get_balance_check(request.args.get("wal"), service) if bc: redeem_lnurl_withdraw(bc.wallet, bc.url) @core_html_routes.get("/lnurlwallet") -async def lnurlwallet(): +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) diff --git a/lnbits/core/views/public_api.py b/lnbits/core/views/public_api.py index 9402d5738..9cf7b6cf8 100644 --- a/lnbits/core/views/public_api.py +++ b/lnbits/core/views/public_api.py @@ -1,7 +1,6 @@ import trio import datetime from http import HTTPStatus -from quart import jsonify from lnbits import bolt11 diff --git a/lnbits/decorators.py b/lnbits/decorators.py index a5a270e13..880ddc5f3 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -1,5 +1,4 @@ from cerberus import Validator # type: ignore -from quart import g, abort, jsonify, request from functools import wraps from http import HTTPStatus from typing import List, Union @@ -77,28 +76,4 @@ def check_user_exists(param: str = "usr"): return wrap -def validate_uuids( - params: List[str], *, required: Union[bool, List[str]] = False, version: int = 4 -): - def wrap(view): - @wraps(view) - 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(): - if not value and (required is True or (required and param in required)): - abort(HTTPStatus.BAD_REQUEST, f"`{param}` is required.") - - if value: - try: - UUID(value, version=version) - except ValueError: - abort(HTTPStatus.BAD_REQUEST, f"`{param}` is not a valid UUID.") - - return await view(**kwargs) - - return wrapped_view - - return wrap diff --git a/lnbits/extensions/offlineshop/lnurl.py b/lnbits/extensions/offlineshop/lnurl.py index ff8b71dcc..8ebf7fa10 100644 --- a/lnbits/extensions/offlineshop/lnurl.py +++ b/lnbits/extensions/offlineshop/lnurl.py @@ -1,5 +1,4 @@ import hashlib -from quart import jsonify, url_for, request from lnurl import LnurlPayResponse, LnurlPayActionResponse, LnurlErrorResponse # type: ignore from lnbits.core.services import create_invoice diff --git a/lnbits/extensions/offlineshop/models.py b/lnbits/extensions/offlineshop/models.py index 8839f0ddc..fa798e5c9 100644 --- a/lnbits/extensions/offlineshop/models.py +++ b/lnbits/extensions/offlineshop/models.py @@ -2,7 +2,7 @@ import json import base64 import hashlib from collections import OrderedDict -from quart import url_for + from typing import Optional, List, Dict from lnurl import encode as lnurl_encode # type: ignore from lnurl.types import LnurlPayMetadata # type: ignore diff --git a/lnbits/extensions/offlineshop/views.py b/lnbits/extensions/offlineshop/views.py index 78ebb799e..9715c76cc 100644 --- a/lnbits/extensions/offlineshop/views.py +++ b/lnbits/extensions/offlineshop/views.py @@ -1,9 +1,8 @@ import time from datetime import datetime -from quart import g, render_template, request from http import HTTPStatus -from lnbits.decorators import check_user_exists, validate_uuids +from lnbits.decorators import check_user_exists from lnbits.core.models import Payment from lnbits.core.crud import get_standalone_payment @@ -15,8 +14,8 @@ from fastapi.templating import Jinja2Templates templates = Jinja2Templates(directory="templates") @offlineshop_ext.get("/") -@validate_uuids(["usr"], required=True) -@check_user_exists() +# @validate_uuids(["usr"], required=True) +# @check_user_exists() async def index(request: Request): return await templates.TemplateResponse("offlineshop/index.html", {"request": request,"user":g.user}) diff --git a/lnbits/extensions/offlineshop/views_api.py b/lnbits/extensions/offlineshop/views_api.py index be860bc0f..284170ef1 100644 --- a/lnbits/extensions/offlineshop/views_api.py +++ b/lnbits/extensions/offlineshop/views_api.py @@ -1,11 +1,12 @@ from typing import Optional from pydantic.main import BaseModel -from quart import g, jsonify + from http import HTTPStatus from lnurl.exceptions import InvalidUrl as LnurlInvalidUrl # type: ignore from lnbits.decorators import api_check_wallet_key, api_validate_post_request from lnbits.utils.exchange_rates import currencies +from lnbits.requestvars import g from . import offlineshop_ext from .crud import ( @@ -27,7 +28,7 @@ async def api_list_currencies_available(): @offlineshop_ext.get("/api/v1/offlineshop") @api_check_wallet_key("invoice") async def api_shop_from_wallet(): - shop = await get_or_create_shop_by_wallet(g.wallet.id) + shop = await get_or_create_shop_by_wallet(g().wallet.id) items = await get_items(shop.id) try: @@ -60,7 +61,7 @@ class CreateItemsData(BaseModel): @offlineshop_ext.put("/api/v1/offlineshop/items/{item_id}") @api_check_wallet_key("invoice") async def api_add_or_update_item(data: CreateItemsData, item_id=None): - shop = await get_or_create_shop_by_wallet(g.wallet.id) + shop = await get_or_create_shop_by_wallet(g().wallet.id) if item_id == None: await add_item( shop.id, @@ -87,7 +88,7 @@ async def api_add_or_update_item(data: CreateItemsData, item_id=None): @offlineshop_ext.delete("/api/v1/offlineshop/items/{item_id}") @api_check_wallet_key("invoice") async def api_delete_item(item_id): - shop = await get_or_create_shop_by_wallet(g.wallet.id) + shop = await get_or_create_shop_by_wallet(g().wallet.id) await delete_item_from_shop(shop.id, item_id) return "", HTTPStatus.NO_CONTENT @@ -104,7 +105,7 @@ async def api_set_method(data: CreateMethodData): wordlist = data.wordlist.split("\n") if data.wordlist else None wordlist = [word.strip() for word in wordlist if word.strip()] - shop = await get_or_create_shop_by_wallet(g.wallet.id) + shop = await get_or_create_shop_by_wallet(g().wallet.id) if not shop: return "", HTTPStatus.NOT_FOUND diff --git a/lnbits/helpers.py b/lnbits/helpers.py index 8b57fe2b5..43623527e 100644 --- a/lnbits/helpers.py +++ b/lnbits/helpers.py @@ -1,11 +1,18 @@ +import glob import json import os -import glob +from typing import Any, List, NamedTuple, Optional + +import jinja2 import shortuuid # type: ignore -from typing import List, NamedTuple, Optional +from lnbits.jinja2_templating import Jinja2Templates +from lnbits.requestvars import g -from .settings import LNBITS_DISABLED_EXTENSIONS, LNBITS_PATH +from .settings import (DEBUG, LNBITS_COMMIT, LNBITS_DISABLED_EXTENSIONS, + LNBITS_PATH, LNBITS_SITE_DESCRIPTION, + LNBITS_SITE_TAGLINE, LNBITS_SITE_TITLE, + LNBITS_THEME_OPTIONS) class Extension(NamedTuple): @@ -132,3 +139,35 @@ def get_vendored(ext: str, prefer_minified: bool = False) -> List[str]: def url_for_vendored(abspath: str) -> str: return "/" + os.path.relpath(abspath, LNBITS_PATH) + +def url_for( + endpoint: str, + external: Optional[bool] = False, + **params: Any, +) -> str: + base = g().base_url if external else "" + url_params = "?" + for key in params: + url_params += f"{key}={params[key]}&" + url = f"{base}{endpoint}{url_params}" + return url + +def template_renderer() -> Jinja2Templates: + t = Jinja2Templates( + loader=jinja2.FileSystemLoader(["lnbits/templates", "lnbits/core/templates"]), + ) + t.env.globals["SITE_TITLE"] = LNBITS_SITE_TITLE + t.env.globals["SITE_TAGLINE"] = LNBITS_SITE_TAGLINE + t.env.globals["SITE_DESCRIPTION"] = LNBITS_SITE_DESCRIPTION + t.env.globals["LNBITS_THEME_OPTIONS"] = LNBITS_THEME_OPTIONS + t.env.globals["LNBITS_VERSION"] = LNBITS_COMMIT + t.env.globals["EXTENSIONS"] = get_valid_extensions() + + if DEBUG: + t.env.globals["VENDORED_JS"] = map(url_for_vendored, get_js_vendored()) + t.env.globals["VENDORED_CSS"] = map(url_for_vendored, get_css_vendored()) + else: + t.env.globals["VENDORED_JS"] = ["/static/bundle.js"] + t.env.globals["VENDORED_CSS"] = ["/static/bundle.css"] + + return t diff --git a/lnbits/tasks.py b/lnbits/tasks.py index d7b297a0f..b6facd603 100644 --- a/lnbits/tasks.py +++ b/lnbits/tasks.py @@ -2,7 +2,6 @@ import time import trio import traceback from http import HTTPStatus -from quart import current_app from typing import List, Callable from lnbits.settings import WALLET diff --git a/lnbits/wallets/lnpay.py b/lnbits/wallets/lnpay.py index bb08d35eb..034d1529e 100644 --- a/lnbits/wallets/lnpay.py +++ b/lnbits/wallets/lnpay.py @@ -4,7 +4,6 @@ import httpx from os import getenv from http import HTTPStatus from typing import Optional, Dict, AsyncGenerator -from quart import request from .base import ( StatusResponse, diff --git a/lnbits/wallets/opennode.py b/lnbits/wallets/opennode.py index 5cc046a5e..ba315503a 100644 --- a/lnbits/wallets/opennode.py +++ b/lnbits/wallets/opennode.py @@ -1,10 +1,10 @@ +from lnbits.helpers import url_for import trio import hmac import httpx from http import HTTPStatus from os import getenv from typing import Optional, AsyncGenerator -from quart import request, url_for from .base import ( StatusResponse, @@ -63,7 +63,7 @@ class OpenNodeWallet(Wallet): json={ "amount": amount, "description": memo or "", - "callback_url": url_for("webhook_listener", _external=True), + "callback_url": url_for("/webhook_listener", _external=True), }, timeout=40, )