taler-merchant-demos

Python-based Frontends for the Demonstration Web site
Log | Files | Refs | Submodules | README | LICENSE

commit d95f211d84f1c02c22c1d054e426959d3e49513d
parent cbe1a1290c399b397a795d49715b9fa03dd5bf52
Author: Florian Dold <florian@dold.me>
Date:   Wed, 16 Oct 2024 14:52:53 +0200

use gunicorn instead of uwsgi to serve the apps

The UWSGI project is in maintainence mode and overkill for what we need.

Diffstat:
MMakefile | 4----
Mdebian/control | 1-
Mpoetry.lock | 273++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Mpyproject.toml | 1+
Atalermerchantdemos/appconfig.py | 27+++++++++++++++++++++++++++
Mtalermerchantdemos/blog/blog.py | 8++------
Mtalermerchantdemos/cli.py | 87++++++++++++++++++++++++++++++++++++++-----------------------------------------
Mtalermerchantdemos/donations/donations.py | 8++------
Mtalermerchantdemos/landing/landing.py | 8++------
9 files changed, 220 insertions(+), 197 deletions(-)

diff --git a/Makefile b/Makefile @@ -19,8 +19,6 @@ setup-arch: echo "Installing Pip Dependencies..." echo "-> Installing wheel..." pip install wheel - echo "-> Installing uwsgi..." - pip install uwsgi echo "-> Installing lxml..." pip install lxml echo "-> Installing poetry..." @@ -40,8 +38,6 @@ setup-deb: echo "Installing Pip Dependencies..." echo "-> Installing wheel..." pip install wheel - echo "-> Installing uwsgi..." - pip install uwsgi echo "-> Installing lxml..." pip install lxml echo "-> Installing poetry..." diff --git a/debian/control b/debian/control @@ -18,7 +18,6 @@ Architecture: all Depends: ${misc:Depends}, python3-flask, python3-flask-babel, - uwsgi, python3-requests, python3-bs4 Recommends: apache2 | nginx | httpd diff --git a/poetry.lock b/poetry.lock @@ -1,10 +1,15 @@ +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. + [[package]] name = "babel" version = "2.9.1" description = "Internationalization utilities" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "Babel-2.9.1-py2.py3-none-any.whl", hash = "sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9"}, + {file = "Babel-2.9.1.tar.gz", hash = "sha256:bc0c176f9f6a994582230df350aa6e05ba2ebe4b3ac317eab29d9be5d2768da0"}, +] [package.dependencies] pytz = ">=2015.7" @@ -13,9 +18,12 @@ pytz = ">=2015.7" name = "beautifulsoup4" version = "4.10.0" description = "Screen-scraping library" -category = "main" optional = false python-versions = ">3.0.0" +files = [ + {file = "beautifulsoup4-4.10.0-py3-none-any.whl", hash = "sha256:9a315ce70049920ea4572a4055bc4bd700c940521d36fc858205ad4fcde149bf"}, + {file = "beautifulsoup4-4.10.0.tar.gz", hash = "sha256:c23ad23c521d818955a4151a67d81580319d4bf548d3d49f4223ae041ff98891"}, +] [package.dependencies] soupsieve = ">1.2" @@ -28,28 +36,37 @@ lxml = ["lxml"] name = "certifi" version = "2021.10.8" description = "Python package for providing Mozilla's CA Bundle." -category = "main" optional = false python-versions = "*" +files = [ + {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, + {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, +] [[package]] name = "charset-normalizer" version = "2.0.7" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" optional = false python-versions = ">=3.5.0" +files = [ + {file = "charset-normalizer-2.0.7.tar.gz", hash = "sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0"}, + {file = "charset_normalizer-2.0.7-py3-none-any.whl", hash = "sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b"}, +] [package.extras] -unicode_backport = ["unicodedata2"] +unicode-backport = ["unicodedata2"] [[package]] name = "click" version = "8.0.3" description = "Composable command line interface toolkit" -category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"}, + {file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"}, +] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} @@ -58,17 +75,23 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "colorama" version = "0.4.4" description = "Cross-platform colored terminal text." -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, +] [[package]] name = "flask" version = "2.0.2" description = "A simple framework for building complex web applications." -category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "Flask-2.0.2-py3-none-any.whl", hash = "sha256:cb90f62f1d8e4dc4621f52106613488b5ba826b2e1e10a33eac92f723093ab6a"}, + {file = "Flask-2.0.2.tar.gz", hash = "sha256:7b2fb8e934ddd50731893bdcdb00fc8c0315916f9fcd50d22c7cc1a95ab634e2"}, +] [package.dependencies] click = ">=7.1.2" @@ -84,9 +107,12 @@ dotenv = ["python-dotenv"] name = "flask-babel" version = "2.0.0" description = "Adds i18n/l10n support to Flask applications" -category = "main" optional = false python-versions = "*" +files = [ + {file = "Flask-Babel-2.0.0.tar.gz", hash = "sha256:f9faf45cdb2e1a32ea2ec14403587d4295108f35017a7821a2b1acb8cfd9257d"}, + {file = "Flask_Babel-2.0.0-py3-none-any.whl", hash = "sha256:e6820a052a8d344e178cdd36dd4bb8aea09b4bda3d5f9fa9f008df2c7f2f5468"}, +] [package.dependencies] Babel = ">=2.3" @@ -95,31 +121,61 @@ Jinja2 = ">=2.5" pytz = "*" [package.extras] -dev = ["pytest", "pytest-mock", "bumpversion", "ghp-import", "sphinx", "pallets-sphinx-themes"] +dev = ["Pallets-Sphinx-Themes", "bumpversion", "ghp-import", "pytest", "pytest-mock", "sphinx"] + +[[package]] +name = "gunicorn" +version = "23.0.0" +description = "WSGI HTTP Server for UNIX" +optional = false +python-versions = ">=3.7" +files = [ + {file = "gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d"}, + {file = "gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec"}, +] + +[package.dependencies] +packaging = "*" + +[package.extras] +eventlet = ["eventlet (>=0.24.1,!=0.36.0)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +testing = ["coverage", "eventlet", "gevent", "pytest", "pytest-cov"] +tornado = ["tornado (>=0.2)"] [[package]] name = "idna" version = "3.3" description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" optional = false python-versions = ">=3.5" +files = [ + {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, + {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, +] [[package]] name = "itsdangerous" version = "2.0.1" description = "Safely pass data to untrusted environments and back." -category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "itsdangerous-2.0.1-py3-none-any.whl", hash = "sha256:5174094b9637652bdb841a3029700391451bd092ba3db90600dea710ba28e97c"}, + {file = "itsdangerous-2.0.1.tar.gz", hash = "sha256:9e724d68fc22902a1435351f84c3fb8623f303fffcc566a4cb952df8c572cff0"}, +] [[package]] name = "jinja2" version = "3.0.2" description = "A very fast and expressive template engine." -category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "Jinja2-3.0.2-py3-none-any.whl", hash = "sha256:8569982d3f0889eed11dd620c706d39b60c36d6d25843961f33f77fb6bc6b20c"}, + {file = "Jinja2-3.0.2.tar.gz", hash = "sha256:827a0e32839ab1600d4eb1c4c33ec5a8edfbc5cb42dafa13b81f182f97784b45"}, +] [package.dependencies] MarkupSafe = ">=2.0" @@ -131,119 +187,9 @@ i18n = ["Babel (>=2.7)"] name = "markupsafe" version = "2.0.1" description = "Safely add untrusted strings to HTML/XML markup." -category = "main" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "pytz" -version = "2021.3" -description = "World timezone definitions, modern and historical" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "requests" -version = "2.26.0" -description = "Python HTTP for Humans." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} -idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} -urllib3 = ">=1.21.1,<1.27" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] -use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] - -[[package]] -name = "soupsieve" -version = "2.2.1" -description = "A modern CSS selector implementation for Beautiful Soup." -category = "main" optional = false python-versions = ">=3.6" - -[[package]] -name = "urllib3" -version = "1.26.7" -description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" - -[package.extras] -brotli = ["brotlipy (>=0.6.0)"] -secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] - -[[package]] -name = "werkzeug" -version = "2.0.2" -description = "The comprehensive WSGI web application library." -category = "main" -optional = false -python-versions = ">=3.6" - -[package.extras] -watchdog = ["watchdog"] - -[metadata] -lock-version = "1.1" -python-versions = "^3.8" -content-hash = "0a8bd3c73fbceb159d7af4b517aedb0d80060648bc5779bc0e05181d4ed4da20" - -[metadata.files] -babel = [ - {file = "Babel-2.9.1-py2.py3-none-any.whl", hash = "sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9"}, - {file = "Babel-2.9.1.tar.gz", hash = "sha256:bc0c176f9f6a994582230df350aa6e05ba2ebe4b3ac317eab29d9be5d2768da0"}, -] -beautifulsoup4 = [ - {file = "beautifulsoup4-4.10.0-py3-none-any.whl", hash = "sha256:9a315ce70049920ea4572a4055bc4bd700c940521d36fc858205ad4fcde149bf"}, - {file = "beautifulsoup4-4.10.0.tar.gz", hash = "sha256:c23ad23c521d818955a4151a67d81580319d4bf548d3d49f4223ae041ff98891"}, -] -certifi = [ - {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, - {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, -] -charset-normalizer = [ - {file = "charset-normalizer-2.0.7.tar.gz", hash = "sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0"}, - {file = "charset_normalizer-2.0.7-py3-none-any.whl", hash = "sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b"}, -] -click = [ - {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"}, - {file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"}, -] -colorama = [ - {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, - {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, -] -flask = [ - {file = "Flask-2.0.2-py3-none-any.whl", hash = "sha256:cb90f62f1d8e4dc4621f52106613488b5ba826b2e1e10a33eac92f723093ab6a"}, - {file = "Flask-2.0.2.tar.gz", hash = "sha256:7b2fb8e934ddd50731893bdcdb00fc8c0315916f9fcd50d22c7cc1a95ab634e2"}, -] -flask-babel = [ - {file = "Flask-Babel-2.0.0.tar.gz", hash = "sha256:f9faf45cdb2e1a32ea2ec14403587d4295108f35017a7821a2b1acb8cfd9257d"}, - {file = "Flask_Babel-2.0.0-py3-none-any.whl", hash = "sha256:e6820a052a8d344e178cdd36dd4bb8aea09b4bda3d5f9fa9f008df2c7f2f5468"}, -] -idna = [ - {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, - {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, -] -itsdangerous = [ - {file = "itsdangerous-2.0.1-py3-none-any.whl", hash = "sha256:5174094b9637652bdb841a3029700391451bd092ba3db90600dea710ba28e97c"}, - {file = "itsdangerous-2.0.1.tar.gz", hash = "sha256:9e724d68fc22902a1435351f84c3fb8623f303fffcc566a4cb952df8c572cff0"}, -] -jinja2 = [ - {file = "Jinja2-3.0.2-py3-none-any.whl", hash = "sha256:8569982d3f0889eed11dd620c706d39b60c36d6d25843961f33f77fb6bc6b20c"}, - {file = "Jinja2-3.0.2.tar.gz", hash = "sha256:827a0e32839ab1600d4eb1c4c33ec5a8edfbc5cb42dafa13b81f182f97784b45"}, -] -markupsafe = [ +files = [ {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"}, {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"}, {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"}, @@ -314,23 +260,92 @@ markupsafe = [ {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, ] -pytz = [ + +[[package]] +name = "packaging" +version = "24.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, +] + +[[package]] +name = "pytz" +version = "2021.3" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ {file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"}, {file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"}, ] -requests = [ + +[[package]] +name = "requests" +version = "2.26.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, ] -soupsieve = [ + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} +idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<5)"] + +[[package]] +name = "soupsieve" +version = "2.2.1" +description = "A modern CSS selector implementation for Beautiful Soup." +optional = false +python-versions = ">=3.6" +files = [ {file = "soupsieve-2.2.1-py3-none-any.whl", hash = "sha256:c2c1c2d44f158cdbddab7824a9af8c4f83c76b1e23e049479aa432feb6c4c23b"}, {file = "soupsieve-2.2.1.tar.gz", hash = "sha256:052774848f448cf19c7e959adf5566904d525f33a3f8b6ba6f6f8f26ec7de0cc"}, ] -urllib3 = [ + +[[package]] +name = "urllib3" +version = "1.26.7" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" +files = [ {file = "urllib3-1.26.7-py2.py3-none-any.whl", hash = "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"}, {file = "urllib3-1.26.7.tar.gz", hash = "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece"}, ] -werkzeug = [ + +[package.extras] +brotli = ["brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "werkzeug" +version = "2.0.2" +description = "The comprehensive WSGI web application library." +optional = false +python-versions = ">=3.6" +files = [ {file = "Werkzeug-2.0.2-py3-none-any.whl", hash = "sha256:63d3dc1cf60e7b7e35e97fa9861f7397283b75d765afcaefd993d6046899de8f"}, {file = "Werkzeug-2.0.2.tar.gz", hash = "sha256:aa2bb6fc8dee8d6c504c0ac1e7f5f7dc5810a9903e793b6f715a9f015bdadb9a"}, ] + +[package.extras] +watchdog = ["watchdog"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.8" +content-hash = "3fb5b3be67c35ccc731b74abc54122853c7074d27c4dad6d2eda0d6cbcaf0864" diff --git a/pyproject.toml b/pyproject.toml @@ -36,6 +36,7 @@ Flask = "^2.0.2" requests = "^2.26.0" beautifulsoup4 = "^4.10.0" Flask-Babel = "^2.0.0" +gunicorn = "^23.0.0" [tool.poetry.dev-dependencies] diff --git a/talermerchantdemos/appconfig.py b/talermerchantdemos/appconfig.py @@ -0,0 +1,27 @@ +## +# This file is part of GNU TALER. +# Copyright (C) 2024 Taler Systems SA +# +# TALER is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free Software +# Foundation; either version 2.1, or (at your option) any later version. +# +# TALER is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License along with +# GNU TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +# +# @author Florian Dold + +import os +from .util.talerconfig import TalerConfig + +def load_taler_config(): + # The (WSGI) host passes the configuration endpoint via + # an environment variable. + config_filename = os.environ["TALER_CONFIG_FILENAME"] + if config_filename == "": + config_filename = None + return TalerConfig.from_file(config_filename) diff --git a/talermerchantdemos/blog/blog.py b/talermerchantdemos/blog/blog.py @@ -24,7 +24,6 @@ import traceback import uuid import base64 import flask -import uwsgi from flask import request, url_for from flask_babel import Babel from flask_babel import refresh @@ -34,6 +33,7 @@ from werkzeug.middleware.proxy_fix import ProxyFix import time import sys from urllib.parse import urljoin, urlencode, urlparse +from ..appconfig import load_taler_config from ..util.talerconfig import TalerConfig, ConfigurationError from ..blog.content import ARTICLES, get_article_contents from talermerchantdemos.httpcommon import ( @@ -65,11 +65,7 @@ app.secret_key = base64.b64encode(os.urandom(64)).decode("utf-8") logging.basicConfig() LOGGER = logging.getLogger(__name__) -config_filename = uwsgi.opt["config_filename"].decode("utf-8") -if config_filename == "": - config_filename = None - -config = TalerConfig.from_file(config_filename) +config = load_taler_config() CURRENCY = config["taler"]["currency"].value_string(required=True) diff --git a/talermerchantdemos/cli.py b/talermerchantdemos/cli.py @@ -27,42 +27,16 @@ import os import site from .util.talerconfig import TalerConfig, ConfigurationError -LOGGER = logging.getLogger(__name__) -# No perfect match to our logging format, but good enough ... -UWSGI_LOGFMT = "%(ltime) %(proto) %(method) %(uri) %(proto) => %(status)" - -arg_venvpath = ["-H", sys.prefix] +import importlib +import multiprocessing +import gunicorn.app.base -# Argument to tell uWSGI to load the python plugin. -# This hack is required, because on systems where the plugin is statically linked, -# loading it causes an error. -arg_load_python = "--if-not-plugin python --plugins python --endif".split(" ") +LOGGER = logging.getLogger(__name__) -## -# This function interprets the 'serve-http' subcommand. -# The effect it to launch the blog HTTP service. -# -# @param args command line options. -def handle_serve_http(config_filename, which_shop, port=None): +def bindspec(config_filename, which_shop, port): config = TalerConfig.from_file(config_filename) confsection = f"frontend-demo-{which_shop}" - currency = config["taler"]["currency"].value_string(required=True) - cfg_arg = "" if config_filename is None else config_filename - params = [ - "uwsgi", - "uwsgi", - *arg_venvpath, - *arg_load_python, - "--master", - "--die-on-term", - "--log-format", - UWSGI_LOGFMT, - "--set-ph", - f"config_filename={cfg_arg}", - "--module", - f"talermerchantdemos.{which_shop}:app", - ] # Takes precedence. if port: @@ -83,22 +57,36 @@ def handle_serve_http(config_filename, which_shop, port=None): if not port_launch: sys.stderr.write("Port number wasn't found in config and in arguments.") exit(1) - params.extend(["--http", f":{port_launch}"]) + return dict(bind=f"0.0.0.0:{port_launch}") if http_serve == "unix": path = config[confsection]["http_unixpath"].value_filename(required=True) mode = config[confsection]["http_unixpath_mode"].value_filename(required=True) - params.extend(["--http-socket", path]) - params.extend(["--chmod-socket=" + mode]) - os.makedirs(os.path.dirname(path), exist_ok=True) - - try: - os.execlp(*params) - except: - sys.stderr.write( - "Failed to start uwsgi. Please make sure to install uwsgi for Python3." - ) - sys.exit(1) + return dict(bind=f"unix:{path}", umask=mode) + + +def number_of_workers(): + return (multiprocessing.cpu_count() * 2) + 1 + + +class StandaloneApplication(gunicorn.app.base.BaseApplication): + + def __init__(self, app, options=None): + self.options = options or {} + self.application = app + super().__init__() + + def load_config(self): + config = { + key: value + for key, value in self.options.items() + if key in self.cfg.settings and value is not None + } + for key, value in config.items(): + self.cfg.set(key.lower(), value) + + def load(self): + return self.application @click.command("Global shop launcher") @@ -116,7 +104,16 @@ def demos(config, http_port, which_shop): if which_shop not in ["blog", "donations", "landing"]: print("Please use a valid shop name: blog, donations, landing.") sys.exit(1) - handle_serve_http(config, which_shop, http_port) + + options = { + "workers": number_of_workers(), + **bindspec(config, which_shop, http_port), + } + mod = f"talermerchantdemos.{which_shop}" + os.environ["TALER_CONFIG_FILENAME"] = config or "" + app = importlib.import_module(mod).app + StandaloneApplication(app, options).run() -demos() +if __name__ == "__main__": + demos() diff --git a/talermerchantdemos/donations/donations.py b/talermerchantdemos/donations/donations.py @@ -20,7 +20,6 @@ import base64 import logging import flask -import uwsgi from flask import request, url_for from flask_babel import Babel from flask_babel import refresh @@ -31,9 +30,9 @@ import os import time import traceback import urllib -from ..util.talerconfig import TalerConfig, ConfigurationError from urllib.parse import urljoin from ..httpcommon import backend_post, backend_get, make_utility_processor, get_locale +from ..appconfig import load_taler_config import sys if not sys.version_info.major == 3 and sys.version_info.minor >= 6: @@ -60,10 +59,7 @@ BABEL_TRANSLATION_DIRECTORIES = "../translations" babel = Babel(app) babel.localeselector(get_locale) -config_filename = uwsgi.opt["config_filename"].decode("utf-8") -if config_filename == "": - config_filename = None -config = TalerConfig.from_file(config_filename) +config = load_taler_config() CURRENCY = config["taler"]["currency"].value_string(required=True) diff --git a/talermerchantdemos/landing/landing.py b/talermerchantdemos/landing/landing.py @@ -22,7 +22,6 @@ import datetime import base64 import logging import flask -import uwsgi import werkzeug from flask import request, url_for from flask_babel import Babel @@ -31,6 +30,7 @@ from flask_babel import force_locale from flask_babel import gettext from werkzeug.middleware.proxy_fix import ProxyFix import traceback +from ..appconfig import load_taler_config from ..util.talerconfig import TalerConfig, ConfigurationError from ..httpcommon import ( backend_get, @@ -58,11 +58,7 @@ app.secret_key = base64.b64encode(os.urandom(64)).decode("utf-8") logging.basicConfig() LOGGER = logging.getLogger(__name__) -config_filename = uwsgi.opt["config_filename"].decode("utf-8") -if config_filename == "": - config_filename = None - -config = TalerConfig.from_file(config_filename) +config = load_taler_config() CURRENCY = config["taler"]["currency"].value_string(required=True)