diff --git a/src/install/frontend.py b/src/install/frontend.py index 80f7079f9..abd08aae8 100644 --- a/src/install/frontend.py +++ b/src/install/frontend.py @@ -1,10 +1,15 @@ import logging import os +import re +import shutil import subprocess from contextlib import suppress from pathlib import Path +from shlex import split from subprocess import PIPE, STDOUT +from packaging.version import parse as parse_version + import config from helperFunctions.install import ( InstallationError, @@ -20,8 +25,10 @@ DEFAULT_CERT = '.\n.\n.\n.\n.\nexample.com\n.\n\n\n' INSTALL_DIR = Path(__file__).parent PIP_DEPENDENCIES = INSTALL_DIR / 'requirements_frontend.txt' -MIME_ICON_DIR = INSTALL_DIR.parent / 'web_interface' / 'static' / 'file_icons' +STATIC_WEB_DIR = INSTALL_DIR.parent / 'web_interface' / 'static' +MIME_ICON_DIR = STATIC_WEB_DIR / 'file_icons' ICON_THEME_INSTALL_PATH = Path('/usr/share/icons/Papirus/24x24') +NODEENV_DIR = 'nodeenv' def execute_commands_and_raise_on_return_code(commands, error=None): @@ -142,6 +149,40 @@ def _copy_mime_icons(): run_cmd_with_logging(f'cp -rL {ICON_THEME_INSTALL_PATH / source} {MIME_ICON_DIR / target}') +def _node_version_is_up_to_date(nodejs_version: str) -> bool: + try: + proc = subprocess.run(split('./nodeenv/bin/node --version'), capture_output=True, text=True, check=True) + installed_version = proc.stdout.strip().lstrip('v') + return installed_version == nodejs_version + except (subprocess.CalledProcessError, OSError): # venv dir exists but node is not installed correctly + return False + + +def _install_nodejs(nodejs_version: str = '22'): + latest_version = _find_latest_node_version(nodejs_version) + with OperateInDirectory(STATIC_WEB_DIR): + if Path(NODEENV_DIR).is_dir() and not _node_version_is_up_to_date(latest_version): + shutil.rmtree(NODEENV_DIR) + + if Path(NODEENV_DIR).is_dir(): + logging.info('Skipping nodeenv installation (already exists)') + else: + run_cmd_with_logging(f'nodeenv {NODEENV_DIR} --node={latest_version} --prebuilt') + run_cmd_with_logging(f'. {NODEENV_DIR}/bin/activate && npm install --no-fund .', shell=True) + + +def _find_latest_node_version(target_version: str) -> str: + proc = subprocess.run(split('nodeenv --list'), capture_output=True, text=True, check=False) + if proc.returncode != 0: + raise InstallationError('nodejs installation failed. Is nodeenv installed?') + available_versions = [ + parse_version(v) for v in re.split(r'[\n\t ]', proc.stderr) if v and v.startswith(target_version) + ] + if not available_versions: + raise InstallationError(f'No nodejs installation candidates found for version "{target_version}"') + return str(max(available_versions)) + + def main(skip_docker, radare, nginx, distribution): if distribution != 'fedora': pkgs = read_package_list_from_file(INSTALL_DIR / 'apt-pkgs-frontend.txt') @@ -157,10 +198,7 @@ def main(skip_docker, radare, nginx, distribution): install_pip_packages(PIP_DEPENDENCIES) - # npm does not allow us to install packages to a specific directory - with OperateInDirectory('../../src/web_interface/static'): - # EBADENGINE can probably be ignored because we probably don't need node. - run_cmd_with_logging('npm install --no-fund .') + _install_nodejs() # create user database _create_directory_for_authentication() diff --git a/src/install/pre_install.sh b/src/install/pre_install.sh index 3534e2eed..7fdeee9c5 100755 --- a/src/install/pre_install.sh +++ b/src/install/pre_install.sh @@ -82,10 +82,6 @@ then fi sudo usermod -aG docker "$FACTUSER" -# Setup npm repository as described in https://github.com/nodesource/distributions#debian-and-ubuntu-based-distributions -curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg --batch --yes -echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_18.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list - IS_VENV=$(python3 -c 'import sys; print(sys.exec_prefix!=sys.base_prefix)') PREFIX="" if [[ $IS_VENV == "False" ]] diff --git a/src/install/requirements_frontend.txt b/src/install/requirements_frontend.txt index fc47c47ba..24461cc5e 100644 --- a/src/install/requirements_frontend.txt +++ b/src/install/requirements_frontend.txt @@ -16,6 +16,9 @@ quantiphy~=2.20 uwsgi~=2.0.25.1 virtualenv~=20.26.1 +# npm installation +nodeenv~=1.8.0 + # must be below dependent packages (flask, flask-login, flask-restx) werkzeug~=3.0.3 diff --git a/src/install/requirements_pre_install.txt b/src/install/requirements_pre_install.txt index 24580206d..3c251b550 100644 --- a/src/install/requirements_pre_install.txt +++ b/src/install/requirements_pre_install.txt @@ -2,6 +2,7 @@ virtualenv # Python Libraries for python based installation distro==1.8.0 +packaging==23.0 python-magic==0.4.27 requests==2.32.2 # Needed by config.py