## # This file is part of GNU Taler # (C) 2017,2021 Taler Systems S.A. # # GNU Taler is free software; you can redistribute it and/or # modify it under the terms of the GNU Affero General Public # License as published by the Free Software Foundation; either # version 3, or (at your option) any later version. # # GNU 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 General Public License for more details. # # You should have received a copy of the GNU General Public # License along with GNU Taler; see the file COPYING. If not, # see # # @author Florian Dold # @file Standalone script to run the blog. import click import logging import argparse import sys 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] # 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(" ") ## # This function interprets the 'serve-uwsgi' subcommand. # The effect is to launch the blog UWSGI service. This # type of service is usually used when the HTTP blog interface # is accessed via a reverse proxy (like Nginx, for example). # # @param command line options. def handle_serve_uwsgi(config, which_shop): serve_uwsgi = config[which_shop]["uwsgi_serve"].value_string(required=True).lower() params = [ "uwsgi", "uwsgi", *arg_venvpath, *arg_load_python, "--master", "--die-on-term", "--log-format", UWSGI_LOGFMT, "--module", "talermerchantdemos.{}:app".format(which_shop), "--need-app", "--set-ph", "backend_url={}".format( config["frontends"]["backend"].value_string(required=True) ), "--set-ph", "currency={}".format( config["taler"]["currency"].value_string(required=True) ), "--set-ph", "apikey={}".format( config["frontends"]["backend_apikey"].value_string(required=True) ), "--module", "--cache2", "name=paid_articles,items=500" ] if serve_uwsgi == "tcp": port = config[which_shop]["uwsgi_port"].value_int(required=True) spec = ":%d" % (port,) params.extend(["--socket", spec]) elif serve_uwsgi == "unix": spec = config[which_shop]["uwsgi_unixpath"].value_filename(required=True) mode = config[which_shop]["uwsgi_unixpath_mode"].value_filename(required=True) params.extend(["--socket", spec]) params.extend(["--chmod-socket=" + mode]) os.makedirs(os.path.dirname(spec), exist_ok=True) logging.info("launching uwsgi with argv %s", params[1:]) try: os.execlp(*params) except: sys.stderr.write( "Failed to start uwsgi. Please make sure to install uwsgi for Python3." ) sys.exit(1) ## # 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, which_shop, port=None): params = [ "uwsgi", "uwsgi", *arg_venvpath, *arg_load_python, "--master", "--die-on-term", "--log-format", UWSGI_LOGFMT, "--set-ph", "backend_url={}".format( config["frontends"]["backend"].value_string(required=True) ), "--set-ph", "currency={}".format( config["taler"]["currency"].value_string(required=True) ), "--set-ph", "apikey={}".format( config["frontends"]["backend_apikey"].value_string(required=True) ), "--module", "talermerchantdemos.{}:app".format(which_shop), ] # Takes precedence. if port: http_serve = "tcp" else: http_serve = config[which_shop]["http_serve"].value_string( required=False, default="tcp" ).lower() if http_serve == "tcp": port_launch = config[which_shop]["http_port"].value_int(required=False) if not port else port 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}"]) if http_serve == "unix": path = config[which_shop]["http_unixpath"].value_filename(required=True) mode = config[which_shop]["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) def handle_serve_from_config(config_obj, which_shop): try: if ( config_obj.value_string(which_shop, "serve", required=True).lower() == "http" ): return handle_serve_http(config_obj, which_shop) handle_serve_uwsgi(config_obj, which_shop) except ConfigurationError as ce: print(ce) exit(1) @click.command("Global shop launcher") @click.option("-c", "--config", help="Configuration file", required=False) @click.option( "--http-port", help="HTTP port to serve (if not given, serving comes from config)", required=False, type=int, ) @click.argument("which-shop") def demos(config, http_port, which_shop): """WHICH_SHOP is one of: blog, donations or landing.""" if which_shop not in ["blog", "donations", "landing"]: print("Please use a valid shop name: blog, donations, landing.") sys.exit(1) config_obj = TalerConfig.from_file(config) if http_port: return handle_serve_http(config_obj, which_shop, http_port) handle_serve_from_config(config_obj, which_shop) demos()