]> git.openstreetmap.org Git - nominatim.git/commitdiff
Merge pull request #3339 from lonvia/python-frontend-as-default
authorSarah Hoffmann <lonvia@denofr.de>
Tue, 20 Feb 2024 09:17:21 +0000 (10:17 +0100)
committerGitHub <noreply@github.com>
Tue, 20 Feb 2024 09:17:21 +0000 (10:17 +0100)
Switch to Python frontend as the default

17 files changed:
.github/workflows/ci-tests.yml
CMakeLists.txt
Vagrantfile
cmake/paths-py-no-php.tmpl [new file with mode: 0644]
docs/admin/Import.md
docs/admin/Installation.md
docs/mkdocs.yml
nominatim/api/logging.py
nominatim/cli.py
nominatim/tokenizer/icu_tokenizer.py
nominatim/tokenizer/legacy_tokenizer.py
nominatim/tools/refresh.py
test/bdd/environment.py
test/bdd/steps/steps_api_queries.py
test/python/cli/test_cli.py
vagrant/Install-on-Ubuntu-20.sh
vagrant/Install-on-Ubuntu-22.sh

index e3d7b4a6a63ca1cf0a3993959244789d38b9183f..910114d7e5cf5e9e0a16d46949b14bbc2f8b5bd9 100644 (file)
@@ -11,7 +11,7 @@ jobs:
               with:
                 submodules: true
 
-            - uses: actions/cache@v3
+            - uses: actions/cache@v4
               with:
                   path: |
                      data/country_osm_grid.sql.gz
@@ -27,7 +27,7 @@ jobs:
                   mv nominatim-src.tar.bz2 Nominatim
 
             - name: 'Upload Artifact'
-              uses: actions/upload-artifact@v3
+              uses: actions/upload-artifact@v4
               with:
                   name: full-source
                   path: nominatim-src.tar.bz2
@@ -43,40 +43,28 @@ jobs:
                       ubuntu: 20
                       postgresql: '9.6'
                       postgis: '2.5'
-                      php: '7.3'
                       lua: '5.1'
                     - flavour: ubuntu-20
                       ubuntu: 20
                       postgresql: 13
                       postgis: 3
-                      php: '7.4'
                       lua: '5.3'
                     - flavour: ubuntu-22
                       ubuntu: 22
                       postgresql: 15
                       postgis: 3
-                      php: '8.1'
                       lua: '5.3'
 
         runs-on: ubuntu-${{ matrix.ubuntu }}.04
 
         steps:
-            - uses: actions/download-artifact@v3
+            - uses: actions/download-artifact@v4
               with:
                   name: full-source
 
             - name: Unpack Nominatim
               run: tar xf nominatim-src.tar.bz2
 
-            - name: Setup PHP
-              uses: shivammathur/setup-php@v2
-              with:
-                  php-version: ${{ matrix.php }}
-                  tools: phpunit:9, phpcs, composer
-                  ini-values: opcache.jit=disable
-              env:
-                  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
             - uses: actions/setup-python@v4
               with:
                 python-version: 3.7
@@ -119,20 +107,11 @@ jobs:
               run: pip3 install -U pylint
               if: matrix.flavour != 'oldstuff'
 
-            - name: PHP linting
-              run: phpcs --report-width=120 .
-              working-directory: Nominatim
-              if: matrix.flavour != 'oldstuff'
-
             - name: Python linting
               run: python3 -m pylint nominatim
               working-directory: Nominatim
               if: matrix.flavour != 'oldstuff'
 
-            - name: PHP unit tests
-              run: phpunit ./
-              working-directory: Nominatim/test/php
-
             - name: Python unit tests
               run: python3 -m pytest test/python
               working-directory: Nominatim
@@ -156,7 +135,7 @@ jobs:
         runs-on: ubuntu-20.04
 
         steps:
-            - uses: actions/download-artifact@v3
+            - uses: actions/download-artifact@v4
               with:
                   name: full-source
 
@@ -185,16 +164,16 @@ jobs:
 
             - name: BDD tests (legacy tokenizer)
               run: |
-                  python3 -m behave -DREMOVE_TEMPLATE=1 -DBUILDDIR=$GITHUB_WORKSPACE/build -DTOKENIZER=legacy --format=progress3
+                  python3 -m behave -DREMOVE_TEMPLATE=1 -DBUILDDIR=$GITHUB_WORKSPACE/build -DAPI_ENGINE=php -DTOKENIZER=legacy --format=progress3
               working-directory: Nominatim/test/bdd
 
 
-    python-api-test:
+    php-test:
         needs: create-archive
         runs-on: ubuntu-22.04
 
         steps:
-            - uses: actions/download-artifact@v3
+            - uses: actions/download-artifact@v4
               with:
                   name: full-source
 
@@ -206,6 +185,23 @@ jobs:
                   postgresql-version: 15
                   postgis-version: 3
 
+            - name: Setup PHP
+              uses: shivammathur/setup-php@v2
+              with:
+                  php-version: 8.1
+                  tools: phpunit:9, phpcs, composer
+                  ini-values: opcache.jit=disable
+              env:
+                  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+            - name: PHP linting
+              run: phpcs --report-width=120 .
+              working-directory: Nominatim
+
+            - name: PHP unit tests
+              run: phpunit ./
+              working-directory: Nominatim/test/php
+
             - uses: ./Nominatim/.github/actions/build-nominatim
               with:
                   flavour: 'ubuntu-22'
@@ -213,12 +209,9 @@ jobs:
             - name: Install test prerequsites
               run: sudo apt-get install -y -qq python3-behave
 
-            - name: Install Python webservers
-              run: pip3 install starlette asgi_lifespan httpx
-
-            - name: BDD tests (starlette)
+            - name: BDD tests (php)
               run: |
-                  python3 -m behave -DREMOVE_TEMPLATE=1 -DBUILDDIR=$GITHUB_WORKSPACE/build -DAPI_ENGINE=starlette --format=progress3
+                  python3 -m behave -DREMOVE_TEMPLATE=1 -DBUILDDIR=$GITHUB_WORKSPACE/build -DAPI_ENGINE=php --format=progress3
               working-directory: Nominatim/test/bdd
 
 
@@ -268,7 +261,7 @@ jobs:
                 OS: ${{ matrix.name }}
                 INSTALL_MODE: ${{ matrix.install_mode }}
 
-            - uses: actions/download-artifact@v3
+            - uses: actions/download-artifact@v4
               with:
                   name: full-source
                   path: /home/nominatim
@@ -355,7 +348,7 @@ jobs:
       needs: create-archive
 
       steps:
-          - uses: actions/download-artifact@v3
+          - uses: actions/download-artifact@v4
             with:
                 name: full-source
 
@@ -392,5 +385,4 @@ jobs:
                 NOMINATIM_DATABASE_DSN="pgsql:host=127.0.0.1;dbname=nominatim;user=osm-import;password=osm-import" nominatim import --continue import-from-file --osm-file test.pbf
 
           - name: Check full import
-            run: |
-              nominatim admin --check-database
+            run: nominatim admin --check-database
index 536b21bc37c7c1c6d53215d5dca95e94998fc2a6..6bd99967351b7b0ea270bc204d953bb5d16f9f53 100644 (file)
@@ -82,13 +82,14 @@ endif()
 
 # Setting PHP binary variable as to command line (prevailing) or auto detect
 
-if (BUILD_API OR BUILD_IMPORTER)
+if (BUILD_API)
     if (NOT PHP_BIN)
          find_program (PHP_BIN php)
     endif()
     # sanity check if PHP binary exists
     if (NOT EXISTS ${PHP_BIN})
-        message(FATAL_ERROR "PHP binary not found. Install php or provide location with -DPHP_BIN=/path/php ")
+        message(WARNING "PHP binary not found. Only Python frontend can be used.")
+        set(PHP_BIN "")
     else()
         message (STATUS "Using PHP binary " ${PHP_BIN})
     endif()
@@ -226,7 +227,11 @@ if (BUILD_IMPORTER)
             PATTERN "paths.py" EXCLUDE
             PATTERN __pycache__ EXCLUDE)
 
-    configure_file(${PROJECT_SOURCE_DIR}/cmake/paths-py.tmpl paths-py.installed)
+    if (EXISTS ${PHP_BIN})
+        configure_file(${PROJECT_SOURCE_DIR}/cmake/paths-py.tmpl paths-py.installed)
+    else()
+        configure_file(${PROJECT_SOURCE_DIR}/cmake/paths-py-no-php.tmpl paths-py.installed)
+    endif()
     install(FILES ${PROJECT_BINARY_DIR}/paths-py.installed
             DESTINATION ${NOMINATIM_LIBDIR}/lib-python/nominatim
             RENAME paths.py)
@@ -254,7 +259,7 @@ if (BUILD_MODULE)
             DESTINATION ${NOMINATIM_LIBDIR}/module)
 endif()
 
-if (BUILD_API)
+if (BUILD_API AND EXISTS ${PHP_BIN})
     install(DIRECTORY lib-php DESTINATION ${NOMINATIM_LIBDIR})
 endif()
 
index 57e64c2c090bcb3addc53e5ffac801f214ddf7d7..7f5f245931269c618d30c6706740b327ec39e66d 100644 (file)
@@ -38,7 +38,7 @@ Vagrant.configure("2") do |config|
     lv.memory = 2048
     lv.nested = true
     if ENV['CHECKOUT'] != 'y' then
-      override.vm.synced_folder ".", "/home/vagrant/Nominatim", type: 'nfs'
+      override.vm.synced_folder ".", "/home/vagrant/Nominatim", type: 'nfs', nfs_udp: false
     end
   end
 
diff --git a/cmake/paths-py-no-php.tmpl b/cmake/paths-py-no-php.tmpl
new file mode 100644 (file)
index 0000000..36856bf
--- /dev/null
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# This file is part of Nominatim. (https://nominatim.org)
+#
+# Copyright (C) 2022 by the Nominatim developer community.
+# For a full list of authors see the git log.
+"""
+Path settings for extra data used by Nominatim (installed version).
+"""
+from pathlib import Path
+
+PHPLIB_DIR = None
+SQLLIB_DIR = (Path('@NOMINATIM_LIBDIR@') / 'lib-sql').resolve()
+DATA_DIR = Path('@NOMINATIM_DATADIR@').resolve()
+CONFIG_DIR = Path('@NOMINATIM_CONFIGDIR@').resolve()
index 0fd5ec29b4256a357fee4b11bb036ec47aa8a0d3..b31066d34799f5b3f355fe57e7561aa6aa9bac92 100644 (file)
@@ -268,18 +268,26 @@ nominatim reverse --lat 51 --lon 45
 ```
 
 If you want to run Nominatim as a service, you need to make a choice between
-running the traditional PHP frontend or the new experimental Python frontend.
+running the modern Python frontend and the legacy PHP frontend.
 Make sure you have installed the right packages as per
 [Installation](Installation.md#software).
 
-#### Testing the PHP frontend
+#### Testing the Python frontend
 
-You can run a small test server with the PHP frontend like this:
+To run the test server against the Python frontend, you must choose a
+web framework to use, either starlette or falcon. Make sure the appropriate
+packages are installed. Then run
 
-```sh
+``` sh
 nominatim serve
 ```
 
+or, if you prefer to use Starlette instead of Falcon as webserver,
+
+``` sh
+nominatim serve --engine starlette
+```
+
 Go to `http://localhost:8088/status.php` and you should see the message `OK`.
 You can also run a search query, e.g. `http://localhost:8088/search.php?q=Berlin`
 or, for reverse-only installations a reverse query,
@@ -287,22 +295,14 @@ e.g. `http://localhost:8088/reverse.php?lat=27.1750090510034&lon=78.04209025`.
 
 Do not use this test server in production.
 To run Nominatim via webservers like Apache or nginx, please continue reading
-[Deploy the PHP frontend](Deployment-PHP.md).
-
-#### Testing the Python frontend
-
-To run the test server against the Python frontend, you must choose a
-web framework to use, either starlette or falcon. Make sure the appropriate
-packages are installed. Then run
+[Deploy the Python frontend](Deployment-Python.md).
 
-``` sh
-nominatim serve --engine falcon
-```
+#### Testing the PHP frontend
 
-or
+You can run a small test server with the PHP frontend like this:
 
-``` sh
-nominatim serve --engine starlette
+```sh
+nominatim serve --engine php
 ```
 
 Go to `http://localhost:8088/status.php` and you should see the message `OK`.
@@ -312,7 +312,8 @@ e.g. `http://localhost:8088/reverse.php?lat=27.1750090510034&lon=78.04209025`.
 
 Do not use this test server in production.
 To run Nominatim via webservers like Apache or nginx, please continue reading
-[Deploy the Python frontend](Deployment-Python.md).
+[Deploy the PHP frontend](Deployment-PHP.md).
+
 
 
 ## Enabling search by category phrases
index 89e56c6e8e165621504e85c308fbfede5057c39b..ef6bd08112532a07c00e1c1577f6a1fc850d4873 100644 (file)
@@ -55,23 +55,24 @@ For running Nominatim:
   * [PyYaml](https://pyyaml.org/) (5.1+)
   * [datrie](https://github.com/pytries/datrie)
 
-When running the PHP frontend:
-
-  * [PHP](https://php.net) (7.3+)
-  * PHP-pgsql
-  * PHP-intl (bundled with PHP)
-
 For running continuous updates:
 
   * [pyosmium](https://osmcode.org/pyosmium/)
 
-For running the experimental Python frontend:
+For running the Python frontend:
 
   * one of the following web frameworks:
     * [falcon](https://falconframework.org/) (3.0+)
     * [starlette](https://www.starlette.io/)
   * [uvicorn](https://www.uvicorn.org/)
 
+For running the legacy PHP frontend:
+
+  * [PHP](https://php.net) (7.3+)
+  * PHP-pgsql
+  * PHP-intl (bundled with PHP)
+
+
 For dependencies for running tests and building documentation, see
 the [Development section](../develop/Development-Environment.md).
 
index f332640ff98f404080df41124fd0bf8b444d5c59..74465d1a45d7b72d8014896f660010ffeb4a1d4f 100644 (file)
@@ -22,8 +22,8 @@ nav:
         - 'Basic Installation': 'admin/Installation.md'
         - 'Import' : 'admin/Import.md'
         - 'Update' : 'admin/Update.md'
-        - 'Deploy (PHP frontend)' : 'admin/Deployment-PHP.md'
         - 'Deploy (Python frontend)' : 'admin/Deployment-Python.md'
+        - 'Deploy (PHP frontend)' : 'admin/Deployment-PHP.md'
         - 'Nominatim UI'  : 'admin/Setup-Nominatim-UI.md'
         - 'Advanced Installations' : 'admin/Advanced-Installations.md'
         - 'Maintenance' : 'admin/Maintenance.md'
index e16e0bd2d3bdbcab64b7f8c074ddbbe72cc4843e..2d9a487ae82cbfcd52b7841f6c416e0dd075c7dc 100644 (file)
@@ -13,6 +13,7 @@ import datetime as dt
 import textwrap
 import io
 import re
+import html
 
 import sqlalchemy as sa
 from sqlalchemy.ext.asyncio import AsyncConnection
@@ -227,7 +228,7 @@ class HTMLLogger(BaseLogger):
                                HtmlFormatter(nowrap=True, lineseparator='<br />'))
             self._write(f'<div class="highlight"><code class="lang-sql">{sqlstr}</code></div>')
         else:
-            self._write(f'<code class="lang-sql">{sqlstr}</code>')
+            self._write(f'<code class="lang-sql">{html.escape(sqlstr)}</code>')
 
 
     def _python_var(self, var: Any) -> str:
@@ -235,7 +236,7 @@ class HTMLLogger(BaseLogger):
             fmt = highlight(str(var), PythonLexer(), HtmlFormatter(nowrap=True))
             return f'<div class="highlight"><code class="lang-python">{fmt}</code></div>'
 
-        return f'<code class="lang-python">{str(var)}</code>'
+        return f'<code class="lang-python">{html.escape(str(var))}</code>'
 
 
     def _write(self, text: str) -> None:
index 88a6078284424b4dc3beacf7d45757eddc1af3bb..720a8ece33e8e126422490735cdd7c055653ea4c 100644 (file)
@@ -159,13 +159,15 @@ class AdminServe:
         group = parser.add_argument_group('Server arguments')
         group.add_argument('--server', default='127.0.0.1:8088',
                            help='The address the server will listen to.')
-        group.add_argument('--engine', default='php',
+        group.add_argument('--engine', default='falcon',
                            choices=('php', 'falcon', 'starlette'),
-                           help='Webserver framework to run. (default: php)')
+                           help='Webserver framework to run. (default: falcon)')
 
 
     def run(self, args: NominatimArgs) -> int:
         if args.engine == 'php':
+            if args.config.lib_dir.php is None:
+                raise UsageError("PHP frontend not configured.")
             run_php_server(args.server, args.project_dir / 'website')
         else:
             import uvicorn # pylint: disable=import-outside-toplevel
index 5a90edf58d849a9b0ec68d528cf42f6ad93d56b6..84b4b9242b995a7b996176fff76c56496c33fc93 100644 (file)
@@ -214,19 +214,20 @@ class ICUTokenizer(AbstractTokenizer):
             return list(s[0].split('@')[0] for s in cur)
 
 
-    def _install_php(self, phpdir: Path, overwrite: bool = True) -> None:
+    def _install_php(self, phpdir: Optional[Path], overwrite: bool = True) -> None:
         """ Install the php script for the tokenizer.
         """
-        assert self.loader is not None
-        php_file = self.data_dir / "tokenizer.php"
-
-        if not php_file.exists() or overwrite:
-            php_file.write_text(dedent(f"""\
-                <?php
-                @define('CONST_Max_Word_Frequency', 10000000);
-                @define('CONST_Term_Normalization_Rules', "{self.loader.normalization_rules}");
-                @define('CONST_Transliteration', "{self.loader.get_search_rules()}");
-                require_once('{phpdir}/tokenizer/icu_tokenizer.php');"""), encoding='utf-8')
+        if phpdir is not None:
+            assert self.loader is not None
+            php_file = self.data_dir / "tokenizer.php"
+
+            if not php_file.exists() or overwrite:
+                php_file.write_text(dedent(f"""\
+                    <?php
+                    @define('CONST_Max_Word_Frequency', 10000000);
+                    @define('CONST_Term_Normalization_Rules', "{self.loader.normalization_rules}");
+                    @define('CONST_Transliteration', "{self.loader.get_search_rules()}");
+                    require_once('{phpdir}/tokenizer/icu_tokenizer.php');"""), encoding='utf-8')
 
 
     def _save_config(self) -> None:
index 2d28a8b29891623e72e7f8c2f0f7b7c9cafbd160..f3a00839aa2f0302ba611f0f0454f7fdf818c02e 100644 (file)
@@ -269,15 +269,16 @@ class LegacyTokenizer(AbstractTokenizer):
     def _install_php(self, config: Configuration, overwrite: bool = True) -> None:
         """ Install the php script for the tokenizer.
         """
-        php_file = self.data_dir / "tokenizer.php"
-
-        if not php_file.exists() or overwrite:
-            php_file.write_text(dedent(f"""\
-                <?php
-                @define('CONST_Max_Word_Frequency', {config.MAX_WORD_FREQUENCY});
-                @define('CONST_Term_Normalization_Rules', "{config.TERM_NORMALIZATION}");
-                require_once('{config.lib_dir.php}/tokenizer/legacy_tokenizer.php');
-                """), encoding='utf-8')
+        if config.lib_dir.php is not None:
+            php_file = self.data_dir / "tokenizer.php"
+
+            if not php_file.exists() or overwrite:
+                php_file.write_text(dedent(f"""\
+                    <?php
+                    @define('CONST_Max_Word_Frequency', {config.MAX_WORD_FREQUENCY});
+                    @define('CONST_Term_Normalization_Rules', "{config.TERM_NORMALIZATION}");
+                    require_once('{config.lib_dir.php}/tokenizer/legacy_tokenizer.php');
+                    """), encoding='utf-8')
 
 
     def _init_db_tables(self, config: Configuration) -> None:
index 43e5b1eb387a4641ef49274bc63eaa485c171eda..008fc7143313469a7962934df9ded6c8415b3e7b 100644 (file)
@@ -213,6 +213,10 @@ def _quote_php_variable(var_type: Type[Any], config: Configuration,
 def setup_website(basedir: Path, config: Configuration, conn: Connection) -> None:
     """ Create the website script stubs.
     """
+    if config.lib_dir.php is None:
+        LOG.info("Python frontend does not require website setup. Skipping.")
+        return
+
     if not basedir.exists():
         LOG.info('Creating website directory.')
         basedir.mkdir()
index 664b5ac79e7d2013182ebff5036f04889870f586..460f3569d3f1a75a91b0e61ba986693f211cbc69 100644 (file)
@@ -28,7 +28,7 @@ userconfig = {
     'SERVER_MODULE_PATH' : None,
     'TOKENIZER' : None, # Test with a custom tokenizer
     'STYLE' : 'extratags',
-    'API_ENGINE': 'php',
+    'API_ENGINE': 'falcon',
     'PHPCOV' : False, # set to output directory to enable code coverage
 }
 
index 3d3b16c76d4a365787413753bb719d96cbf44bf5..aa1b43b8493d387a7323c1fc5ff6b67eb5e7e53e 100644 (file)
@@ -243,7 +243,7 @@ def step_impl(context, fmt):
         try:
             tree = ET.fromstring(context.response.page)
         except Exception as ex:
-            assert False, f"Could not parse page:\n{context.response.page}"
+            assert False, f"Could not parse page: {ex}\n{context.response.page}"
 
         assert tree.tag == 'html'
         body = tree.find('./body')
index 93e8610887a94dd501d1b4fd3a203ed417267851..d455f35e1157b3aadb53b8e715e1ab211b9deb9c 100644 (file)
@@ -63,7 +63,7 @@ def test_cli_add_data_tiger_data(cli_call, cli_tokenizer_mock, mock_func_factory
 def test_cli_serve_php(cli_call, mock_func_factory):
     func = mock_func_factory(nominatim.cli, 'run_php_server')
 
-    cli_call('serve') == 0
+    cli_call('serve', '--engine', 'php') == 0
 
     assert func.called == 1
 
index 720e80c890161296e520841004bffb58db564f61..57361ec7755cf1c2047120d6c3d1d768b680e1b6 100755 (executable)
@@ -16,16 +16,16 @@ export DEBIAN_FRONTEND=noninteractive #DOCS:
 # Make sure all packages are up-to-date by running:
 #
 
-    sudo apt update -qq
+    sudo apt-get update -qq
 
 # Now you can install all packages needed for Nominatim:
 
-    sudo apt install -y build-essential cmake g++ libboost-dev libboost-system-dev \
+    sudo apt-get install -y build-essential cmake g++ libboost-dev libboost-system-dev \
                         libboost-filesystem-dev libexpat1-dev zlib1g-dev \
                         libbz2-dev libpq-dev liblua5.3-dev lua5.3 lua-dkjson \
                         nlohmann-json3-dev postgresql-12-postgis-3 \
                         postgresql-contrib-12 postgresql-12-postgis-3-scripts \
-                        php-cli php-pgsql php-intl libicu-dev python3-dotenv \
+                        libicu-dev python3-dotenv \
                         python3-psycopg2 python3-psutil python3-jinja2 python3-pip \
                         python3-icu python3-datrie python3-yaml git
 
@@ -133,45 +133,107 @@ fi                                 #DOCS:
 
 # Nominatim is now ready to use. You can continue with
 # [importing a database from OSM data](../admin/Import.md). If you want to set up
-# a webserver first, continue reading.
+# the API frontend first, continue reading.
 #
+# Setting up the Python frontend
+# ==============================
+#
+# Some of the Python packages in Ubuntu are too old. Therefore run the
+# frontend from a Python virtualenv with current packages.
+#
+# To set up the virtualenv, run:
+
+#DOCS:```sh
+sudo apt-get install -y virtualenv
+virtualenv $USERHOME/nominatim-venv
+$USERHOME/nominatim-venv/bin/pip install SQLAlchemy PyICU psycopg[binary] \
+              psycopg2-binary python-dotenv PyYAML falcon uvicorn gunicorn
+#DOCS:```
+
+# Next you need to create a systemd job that runs Nominatim on gunicorn.
+# First create a systemd job that manages the socket file:
+
+#DOCS:```sh
+sudo tee /etc/systemd/system/nominatim.socket << EOFSOCKETSYSTEMD
+[Unit]
+Description=Gunicorn socket for Nominatim
+
+[Socket]
+ListenStream=/run/nominatim.sock
+SocketUser=www-data
+
+[Install]
+WantedBy=multi-user.target
+EOFSOCKETSYSTEMD
+#DOCS:```
+
+# Then create the service for Nominatim itself.
+
+#DOCS:```sh
+sudo tee /etc/systemd/system/nominatim.service << EOFNOMINATIMSYSTEMD
+[Unit]
+Description=Nominatim running as a gunicorn application
+After=network.target
+Requires=nominatim.socket
+
+[Service]
+Type=simple
+Environment="PYTHONPATH=/usr/local/lib/nominatim/lib-python/"
+User=www-data
+Group=www-data
+WorkingDirectory=$USERHOME/nominatim-project
+ExecStart=$USERHOME/nominatim-venv/bin/gunicorn -b unix:/run/nominatim.sock -w 4 -k uvicorn.workers.UvicornWorker nominatim.server.falcon.server:run_wsgi
+ExecReload=/bin/kill -s HUP \$MAINPID
+StandardOutput=append:/var/log/gunicorn-nominatim.log
+StandardError=inherit
+PrivateTmp=true
+TimeoutStopSec=5
+KillMode=mixed
+
+[Install]
+WantedBy=multi-user.target
+EOFNOMINATIMSYSTEMD
+#DOCS:```
+
+# Activate the services:
+
+if [ "x$NOSYSTEMD" != "xyes" ]; then  #DOCS:
+    sudo systemctl daemon-reload
+    sudo systemctl enable nominatim.socket
+    sudo systemctl start nominatim.socket
+    sudo systemctl enable nominatim.service
+fi                                    #DOCS:
+
+
 # Setting up a webserver
 # ======================
 #
-# The webserver should serve the php scripts from the website directory of your
-# [project directory](../admin/Import.md#creating-the-project-directory).
-# This directory needs to exist when being configured.
-# Therefore set up a project directory and create a website directory:
+# The webserver is only needed as a proxy between the public interface
+# and the gunicorn service.
+#
+# The frontend will need configuration information from the project
+# directory, which will be populated later
+# [during the import process](../admin/Import.md#creating-the-project-directory)
+# Already create the project directory itself now:
+
 
     mkdir $USERHOME/nominatim-project
-    mkdir $USERHOME/nominatim-project/website
 
-# The import process will populate the directory later.
 
-#
 # Option 1: Using Apache
 # ----------------------
 #
 if [ "x$2" == "xinstall-apache" ]; then #DOCS:
-#
-# Apache has a PHP module that can be used to serve Nominatim. To install them
-# run:
+# First install apache itself and enable the proxy module:
 
-    sudo apt install -y apache2 libapache2-mod-php
+    sudo apt-get install -y apache2
+    sudo a2enmod proxy_http
 
-# You need to create an alias to the website directory in your apache
-# configuration. Add a separate nominatim configuration to your webserver:
+# To set up proxying for Apache add the following configuration:
 
 #DOCS:```sh
 sudo tee /etc/apache2/conf-available/nominatim.conf << EOFAPACHECONF
-<Directory "$USERHOME/nominatim-project/website">
-  Options FollowSymLinks MultiViews
-  AddType text/html   .php
-  DirectoryIndex search.php
-  Require all granted
-</Directory>
-
-Alias /nominatim $USERHOME/nominatim-project/website
+ProxyPass /nominatim "unix:/run/nominatim.sock|http://localhost/"
 EOFAPACHECONF
 #DOCS:```
 
@@ -196,33 +258,9 @@ fi   #DOCS:
 #
 if [ "x$2" == "xinstall-nginx" ]; then #DOCS:
 
-# Nginx has no native support for php scripts. You need to set up php-fpm for
-# this purpose. First install nginx and php-fpm:
-
-    sudo apt install -y nginx php-fpm
+# First install nginx itself:
 
-# You need to configure php-fpm to listen on a Unix socket.
-
-#DOCS:```sh
-sudo tee /etc/php/7.4/fpm/pool.d/www.conf << EOF_PHP_FPM_CONF
-[www]
-; Replace the tcp listener and add the unix socket
-listen = /var/run/php-fpm-nominatim.sock
-
-; Ensure that the daemon runs as the correct user
-listen.owner = www-data
-listen.group = www-data
-listen.mode = 0666
-
-; Unix user of FPM processes
-user = www-data
-group = www-data
-
-; Choose process manager type (static, dynamic, ondemand)
-pm = ondemand
-pm.max_children = 5
-EOF_PHP_FPM_CONF
-#DOCS:```
+    sudo apt-get install -y nginx
 
 # Then create a Nginx configuration to forward http requests to that socket.
 
@@ -233,45 +271,26 @@ server {
     listen [::]:80 default_server;
 
     root $USERHOME/nominatim-project/website;
-    index search.php index.html;
-    location / {
-        try_files \$uri \$uri/ @php;
-    }
-
-    location @php {
-        fastcgi_param SCRIPT_FILENAME "\$document_root\$uri.php";
-        fastcgi_param PATH_TRANSLATED "\$document_root\$uri.php";
-        fastcgi_param QUERY_STRING    \$args;
-        fastcgi_pass unix:/var/run/php-fpm-nominatim.sock;
-        fastcgi_index index.php;
-        include fastcgi_params;
-    }
-
-    location ~ [^/]\.php(/|$) {
-        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
-        if (!-f \$document_root\$fastcgi_script_name) {
-            return 404;
-        }
-        fastcgi_pass unix:/var/run/php-fpm-nominatim.sock;
-        fastcgi_index search.php;
-        include fastcgi.conf;
+    index /search;
+
+    location /nominatim/ {
+            proxy_set_header Host \$http_host;
+            proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
+            proxy_set_header X-Forwarded-Proto \$scheme;
+            proxy_redirect off;
+            proxy_pass http://unix:/run/nominatim.sock:/;
     }
 }
 EOF_NGINX_CONF
 #DOCS:```
 
-# If you have some errors, make sure that php-fpm-nominatim.sock is well under
-# /var/run/ and not under /var/run/php. Otherwise change the Nginx configuration
-# to /var/run/php/php-fpm-nominatim.sock.
-#
 # Enable the configuration and restart Nginx
 #
 
 if [ "x$NOSYSTEMD" == "xyes" ]; then  #DOCS:
-    sudo /usr/sbin/php-fpm7.4 --nodaemonize --fpm-config /etc/php/7.4/fpm/php-fpm.conf & #DOCS:
     sudo /usr/sbin/nginx &            #DOCS:
 else                                  #DOCS:
-    sudo systemctl restart php7.4-fpm nginx
+    sudo systemctl restart nginx
 fi                                    #DOCS:
 
 # The Nominatim API is now available at `http://localhost/`.
index 174b8a771ab8ef95d277850c8699c7476ee9f48a..ae0c0dea25b12f09b172e11af2fe487ddb8cd033 100755 (executable)
@@ -16,19 +16,20 @@ export DEBIAN_FRONTEND=noninteractive #DOCS:
 # Make sure all packages are up-to-date by running:
 #
 
-    sudo apt update -qq
+    sudo apt-get update -qq
 
 # Now you can install all packages needed for Nominatim:
 
-    sudo apt install -y build-essential cmake g++ libboost-dev libboost-system-dev \
+    sudo apt-get install -y build-essential cmake g++ libboost-dev libboost-system-dev \
                         libboost-filesystem-dev libexpat1-dev zlib1g-dev \
                         libbz2-dev libpq-dev liblua5.3-dev lua5.3 lua-dkjson \
                         nlohmann-json3-dev postgresql-14-postgis-3 \
                         postgresql-contrib-14 postgresql-14-postgis-3-scripts \
-                        php-cli php-pgsql php-intl libicu-dev python3-dotenv \
+                        libicu-dev python3-dotenv \
                         python3-psycopg2 python3-psutil python3-jinja2 \
-                        python3-icu python3-datrie python3-sqlalchemy \
-                        python3-asyncpg python3-yaml git
+                        python3-sqlalchemy python3-asyncpg \
+                        python3-icu python3-datrie python3-yaml git
+
 
 #
 # System Configuration
@@ -128,20 +129,89 @@ fi                                 #DOCS:
 
 # Nominatim is now ready to use. You can continue with
 # [importing a database from OSM data](../admin/Import.md). If you want to set up
-# a webserver first, continue reading.
+# the API frontend first, continue reading.
+#
+# Setting up the Python frontend
+# ==============================
+#
+# Some of the Python packages in Ubuntu are too old. Therefore run the
+# frontend from a Python virtualenv with current packages.
 #
+# To set up the virtualenv, run:
+
+#DOCS:```sh
+sudo apt-get install -y virtualenv
+virtualenv $USERHOME/nominatim-venv
+$USERHOME/nominatim-venv/bin/pip install SQLAlchemy PyICU psycopg[binary] \
+              psycopg2-binary python-dotenv PyYAML falcon uvicorn gunicorn
+#DOCS:```
+
+# Next you need to create a systemd job that runs Nominatim on gunicorn.
+# First create a systemd job that manages the socket file:
+
+#DOCS:```sh
+sudo tee /etc/systemd/system/nominatim.socket << EOFSOCKETSYSTEMD
+[Unit]
+Description=Gunicorn socket for Nominatim
+
+[Socket]
+ListenStream=/run/nominatim.sock
+SocketUser=www-data
+
+[Install]
+WantedBy=multi-user.target
+EOFSOCKETSYSTEMD
+#DOCS:```
+
+# Then create the service for Nominatim itself.
+
+#DOCS:```sh
+sudo tee /etc/systemd/system/nominatim.service << EOFNOMINATIMSYSTEMD
+[Unit]
+Description=Nominatim running as a gunicorn application
+After=network.target
+Requires=nominatim.socket
+
+[Service]
+Type=simple
+Environment="PYTHONPATH=/usr/local/lib/nominatim/lib-python/"
+User=www-data
+Group=www-data
+WorkingDirectory=$USERHOME/nominatim-project
+ExecStart=$USERHOME/nominatim-venv/bin/gunicorn -b unix:/run/nominatim.sock -w 4 -k uvicorn.workers.UvicornWorker nominatim.server.falcon.server:run_wsgi
+ExecReload=/bin/kill -s HUP \$MAINPID
+StandardOutput=append:/var/log/gunicorn-nominatim.log
+StandardError=inherit
+PrivateTmp=true
+TimeoutStopSec=5
+KillMode=mixed
+
+[Install]
+WantedBy=multi-user.target
+EOFNOMINATIMSYSTEMD
+#DOCS:```
+
+# Activate the services:
+
+if [ "x$NOSYSTEMD" != "xyes" ]; then  #DOCS:
+    sudo systemctl daemon-reload
+    sudo systemctl enable nominatim.socket
+    sudo systemctl start nominatim.socket
+    sudo systemctl enable nominatim.service
+fi                                    #DOCS:
+
 # Setting up a webserver
 # ======================
 #
-# The webserver should serve the php scripts from the website directory of your
-# [project directory](../admin/Import.md#creating-the-project-directory).
-# This directory needs to exist when being configured.
-# Therefore set up a project directory and create a website directory:
+# The webserver is only needed as a proxy between the public interface
+# and the gunicorn service.
+#
+# The frontend will need configuration information from the project
+# directory, which will be populated later
+# [during the import process](../admin/Import.md#creating-the-project-directory)
+# Already create the project directory itself now:
 
     mkdir $USERHOME/nominatim-project
-    mkdir $USERHOME/nominatim-project/website
-
-# The import process will populate the directory later.
 
 #
 # Option 1: Using Apache
@@ -149,24 +219,18 @@ fi                                 #DOCS:
 #
 if [ "x$2" == "xinstall-apache" ]; then #DOCS:
 #
-# Apache has a PHP module that can be used to serve Nominatim. To install them
-# run:
+# First install apache itself and enable the proxy module:
 
-    sudo apt install -y apache2 libapache2-mod-php
+    sudo apt-get install -y apache2
+    sudo a2enmod proxy_http
 
-# You need to create an alias to the website directory in your apache
-# configuration. Add a separate nominatim configuration to your webserver:
+#
+# To set up proxying for Apache add the following configuration:
 
 #DOCS:```sh
 sudo tee /etc/apache2/conf-available/nominatim.conf << EOFAPACHECONF
-<Directory "$USERHOME/nominatim-project/website">
-  Options FollowSymLinks MultiViews
-  AddType text/html   .php
-  DirectoryIndex search.php
-  Require all granted
-</Directory>
-
-Alias /nominatim $USERHOME/nominatim-project/website
+
+ProxyPass /nominatim "unix:/run/nominatim.sock|http://localhost/"
 EOFAPACHECONF
 #DOCS:```
 
@@ -174,7 +238,10 @@ EOFAPACHECONF
 # Then enable the configuration and restart apache
 #
 
-    sudo a2enconf nominatim
+#DOCS:```sh
+sudo a2enconf nominatim
+#DOCS:```
+
 if [ "x$NOSYSTEMD" == "xyes" ]; then  #DOCS:
     sudo apache2ctl start             #DOCS:
 else                                  #DOCS:
@@ -191,33 +258,10 @@ fi   #DOCS:
 #
 if [ "x$2" == "xinstall-nginx" ]; then #DOCS:
 
-# Nginx has no native support for php scripts. You need to set up php-fpm for
-# this purpose. First install nginx and php-fpm:
+# First install nginx itself:
 
-    sudo apt install -y nginx php-fpm
+    sudo apt-get install -y nginx
 
-# You need to configure php-fpm to listen on a Unix socket.
-
-#DOCS:```sh
-sudo tee /etc/php/8.1/fpm/pool.d/www.conf << EOF_PHP_FPM_CONF
-[www]
-; Replace the tcp listener and add the unix socket
-listen = /var/run/php-fpm-nominatim.sock
-
-; Ensure that the daemon runs as the correct user
-listen.owner = www-data
-listen.group = www-data
-listen.mode = 0666
-
-; Unix user of FPM processes
-user = www-data
-group = www-data
-
-; Choose process manager type (static, dynamic, ondemand)
-pm = ondemand
-pm.max_children = 5
-EOF_PHP_FPM_CONF
-#DOCS:```
 
 # Then create a Nginx configuration to forward http requests to that socket.
 
@@ -228,49 +272,28 @@ server {
     listen [::]:80 default_server;
 
     root $USERHOME/nominatim-project/website;
-    index search.php index.html;
-    location / {
-        try_files \$uri \$uri/ @php;
-    }
-
-    location @php {
-        fastcgi_param SCRIPT_FILENAME "\$document_root\$uri.php";
-        fastcgi_param PATH_TRANSLATED "\$document_root\$uri.php";
-        fastcgi_param QUERY_STRING    \$args;
-        fastcgi_pass unix:/var/run/php-fpm-nominatim.sock;
-        fastcgi_index index.php;
-        include fastcgi_params;
-    }
-
-    location ~ [^/]\.php(/|$) {
-        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
-        if (!-f \$document_root\$fastcgi_script_name) {
-            return 404;
-        }
-        fastcgi_pass unix:/var/run/php-fpm-nominatim.sock;
-        fastcgi_index search.php;
-        include fastcgi.conf;
+    index /search;
+
+    location /nominatim/ {
+            proxy_set_header Host \$http_host;
+            proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
+            proxy_set_header X-Forwarded-Proto \$scheme;
+            proxy_redirect off;
+            proxy_pass http://unix:/run/nominatim.sock:/;
     }
 }
 EOF_NGINX_CONF
 #DOCS:```
 
-# If you have some errors, make sure that php-fpm-nominatim.sock is well under
-# /var/run/ and not under /var/run/php. Otherwise change the Nginx configuration
-# to /var/run/php/php-fpm-nominatim.sock.
-#
 # Enable the configuration and restart Nginx
 #
 
 if [ "x$NOSYSTEMD" == "xyes" ]; then  #DOCS:
-    sudo /usr/sbin/php-fpm8.1 --nodaemonize --fpm-config /etc/php/8.1/fpm/php-fpm.conf & #DOCS:
     sudo /usr/sbin/nginx &            #DOCS:
 else                                  #DOCS:
-    sudo systemctl restart php8.1-fpm nginx
+    sudo systemctl restart nginx
 fi                                    #DOCS:
 
-# The Nominatim API is now available at `http://localhost/`.
-
-
+# The Nominatim API is now available at `http://localhost/nominatim/`.
 
 fi   #DOCS: