diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index fb2e7e186093..9201540632cb 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -880,7 +880,6 @@ ./services/misc/ihaskell.nix ./services/misc/iio-niri.nix ./services/misc/input-remapper.nix - ./services/misc/inventree.nix ./services/misc/invidious-router.nix ./services/misc/irkerd.nix ./services/misc/jackett.nix diff --git a/nixos/modules/services/misc/inventree.nix b/nixos/modules/services/misc/inventree.nix deleted file mode 100644 index 15476e20b167..000000000000 --- a/nixos/modules/services/misc/inventree.nix +++ /dev/null @@ -1,446 +0,0 @@ -{ - config, - pkgs, - lib, - ... -}: -let - cfg = config.services.inventree; - pkg = cfg.package; - - mysqlLocal = cfg.database.createLocally && cfg.database.dbtype == "mysql"; - pgsqlLocal = cfg.database.createLocally && cfg.database.dbtype == "postgresql"; - - manage = pkgs.writeShellScriptBin "inventree-manage" '' - set -a - ${lib.toShellVars cfg.settings} - ${lib.optionalString ( - cfg.database.passwordFile != null - ) ''INVENTREE_DB_PASSWORD="$(<"${cfg.database.passwordFile}")"''} - set +a - - pushd "${cfg.dataDir}" - sudo=exec - if [[ "$USER" != ${cfg.user} ]]; then - ${ - if config.security.sudo.enable then - "sudo='exec ${config.security.wrapperDir}/sudo -u ${cfg.user} -E'" - else - ">&2 echo 'Aborting, inventree-manage must be run as user `${cfg.user}`!'; exit 2" - } - fi - $sudo ${cfg.package}/bin/inventree "$@" - popd - ''; - -in -{ - meta.buildDocsInSandbox = false; - meta.maintainers = with lib.maintainers; [ - kurogeek - ]; - - options.services.inventree = with lib; { - enable = lib.mkEnableOption "inventree"; - - dataDir = mkOption { - type = types.str; - default = "/var/lib/inventree"; - description = "Inventree's data storage path. Will be `/var/lib/inventree` by default."; - }; - - package = mkOption { - type = types.package; - description = "Which package to use for the InvenTree instance."; - default = pkgs.inventree; - defaultText = literalExpression "pkgs.inventree"; - }; - - adminPasswordFile = mkOption { - type = types.nullOr lib.types.path; - default = null; - example = "/run/keys/inventree-password"; - description = "Path to a file containing admin password"; - }; - - secretKeyFile = mkOption { - type = types.path; - default = "${cfg.dataDir}/secret_key.txt"; - defaultText = lib.literalExpression ''"''${cfg.dataDir}/secret_key.txt"''; - example = "/run/keys/inventree-secret-key"; - description = '' - Path to a file containing the secret key - ''; - }; - - database = { - dbtype = mkOption { - type = lib.types.nullOr ( - lib.types.enum [ - "postgresql" - "mysql" - ] - ); - default = "postgresql"; - description = "Database type."; - }; - dbhost = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = - if pgsqlLocal then - "/run/postgresql" - else if mysqlLocal then - "/run/mysqld/mysqld.sock" - else - "localhost"; - defaultText = "localhost"; - example = "localhost"; - description = '' - Database host or socket path. - If [](#opt-services.inventree.database.createLocally) is true and - [](#opt-services.inventree.database.dbtype) is either `postgresql` or `mysql`, - defaults to the correct Unix socket instead. - ''; - }; - dbport = mkOption { - type = types.port; - default = 5432; - description = "Database host port."; - }; - dbname = mkOption { - type = types.str; - default = "inventree"; - description = "Database name."; - }; - dbuser = mkOption { - type = types.str; - default = "inventree"; - description = "Database username."; - }; - passwordFile = mkOption { - type = with types; nullOr path; - default = null; - example = "/run/keys/inventree-dbpassword"; - description = '' - A file containing the password corresponding to - . - ''; - }; - createLocally = mkOption { - type = types.bool; - default = true; - description = "Create the database and database user locally."; - }; - }; - - domain = mkOption { - type = types.str; - default = "localhost"; - example = "inventree.example.com"; - description = '' - The INVENTREE_SITE_URL option defines the base URL for the - InvenTree server. This is a critical setting, and it is required - for correct operation of the server. If not specified, the - server will attempt to determine the site URL automatically - - but this may not always be correct! - - The site URL is the URL that users will use to access the - InvenTree server. For example, if the server is accessible at - `https://inventree.example.com`, the site URL should be set to - `https://inventree.example.com`. Note that this is not - necessarily the same as the internal URL that the server is - running on - the internal URL will depend entirely on your - server configuration and may be obscured by a reverse proxy or - other such setup. - ''; - }; - - user = mkOption { - type = types.str; - default = "inventree"; - description = "User under which InvenTree runs."; - }; - - group = mkOption { - type = types.str; - default = "inventree"; - description = "Group under which InvenTree runs."; - }; - - settings = mkOption { - type = - with lib.types; - attrsOf ( - nullOr (oneOf [ - path - str - ]) - ); - default = { }; - description = '' - InvenTree config options. - - See [the documentation](https://docs.inventree.org/en/stable/start/config/) for available options. - ''; - example = { - INVENTREE_CACHE_ENABLED = true; - INVENTREE_CACHE_HOST = "localhost"; - - INVENTREE_EMAIL_HOST = "smtp.example.com"; - INVENTREE_EMAIL_PORT = 25; - }; - }; - - }; - - config = lib.mkIf cfg.enable ( - lib.mkMerge [ - { - services.inventree.settings = { - INVENTREE_DB_ENGINE = cfg.database.dbtype; - INVENTREE_DB_NAME = cfg.database.dbname; - INVENTREE_DB_HOST = cfg.database.dbhost; - INVENTREE_DB_USER = cfg.database.dbuser; - INVENTREE_DB_PORT = toString cfg.database.dbport; - - INVENTREE_CONFIG_FILE = lib.mkDefault "${cfg.dataDir}/config/config.yaml"; - INVENTREE_OIDC_PRIVATE_KEY_FILE = lib.mkDefault "${cfg.dataDir}/config/oidc_private_key.txt"; - INVENTREE_STATIC_ROOT = lib.mkDefault "${cfg.dataDir}/data/static_root"; - INVENTREE_MEDIA_ROOT = lib.mkDefault "${cfg.dataDir}/data/media"; - INVENTREE_BACKUP_DIR = lib.mkDefault "${cfg.dataDir}/data/backups"; - INVENTREE_SITE_URL = lib.mkDefault "http://${cfg.domain}"; - INVENTREE_PLUGIN_FILE = lib.mkDefault "${cfg.dataDir}/data/plugins/plugins.txt"; - INVENTREE_PLUGIN_DIR = lib.mkDefault "${cfg.dataDir}/data/plugins"; - INVENTREE_ADMIN_PASSWORD_FILE = lib.mkDefault cfg.adminPasswordFile; - INVENTREE_SECRET_KEY_FILE = lib.mkDefault cfg.secretKeyFile; - INVENTREE_AUTO_UPDATE = lib.mkDefault "false"; - }; - environment.systemPackages = [ manage ]; - systemd.tmpfiles.rules = ( - map (dir: "d ${dir} 0755 inventree inventree") [ - "${cfg.dataDir}" - "${cfg.dataDir}/config" - "${cfg.dataDir}/data" - "${cfg.dataDir}/data/static_root" - "${cfg.dataDir}/data/media" - "${cfg.dataDir}/data/backups" - "${cfg.dataDir}/data/plugins" - ] - ); - - services.postgresql = lib.mkIf pgsqlLocal { - enable = true; - ensureDatabases = [ cfg.database.dbname ]; - ensureUsers = [ - { - name = cfg.database.dbuser; - ensureDBOwnership = true; - } - ]; - }; - - services.mysql = lib.mkIf mysqlLocal { - enable = true; - package = lib.mkDefault pkgs.mariadb; - ensureDatabases = [ cfg.database.dbname ]; - ensureUsers = [ - { - name = cfg.database.dbuser; - ensurePermissions = { - "${cfg.database.dbname}.*" = "ALL PRIVILEGES"; - }; - } - ]; - }; - - services.nginx.enable = true; - services.nginx.virtualHosts.${cfg.domain} = { - locations = - let - unixPath = config.systemd.sockets.inventree-server.socketConfig.ListenStream; - in - { - "/" = { - extraConfig = '' - proxy_set_header Host $host; - proxy_set_header X-Forwarded-By $server_addr:$server_port; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header CLIENT_IP $remote_addr; - - proxy_pass_request_headers on; - - proxy_redirect off; - - client_max_body_size 100M; - - proxy_buffering off; - proxy_request_buffering off; - ''; - proxyPass = "http://unix:${unixPath}"; - }; - "/auth" = { - extraConfig = '' - internal; - proxy_pass_request_body off; - proxy_set_header Content-Length ""; - proxy_set_header X-Original-URI $request_uri; - ''; - proxyPass = "http://unix:${unixPath}:/auth/"; - }; - "/static/" = { - alias = "${cfg.settings.INVENTREE_STATIC_ROOT}/"; - extraConfig = '' - autoindex on; - - # Caching settings - expires 30d; - add_header Pragma public; - add_header Cache-Control "public"; - ''; - }; - "/media/" = { - alias = "${cfg.settings.INVENTREE_MEDIA_ROOT}/"; - extraConfig = '' - auth_request /auth; - add_header Content-disposition "attachment"; - ''; - }; - }; - }; - - systemd.services.inventree-setup = { - description = "Inventree setup"; - wantedBy = [ "inventree.target" ]; - partOf = [ "inventree.target" ]; - after = lib.optional mysqlLocal "mysql.service" ++ lib.optional pgsqlLocal "postgresql.target"; - requires = lib.optional mysqlLocal "mysql.service" ++ lib.optional pgsqlLocal "postgresql.target"; - before = [ - "inventree-static.service" - "inventree-server.service" - "inventree-qcluster.service" - ]; - serviceConfig = { - Type = "oneshot"; - User = cfg.user; - Group = cfg.group; - RemainAfterExit = true; - PrivateTmp = true; - } - // lib.optionalAttrs (cfg.database.passwordFile != null) { - LoadCredential = "db_password:${cfg.database.passwordFile}"; - }; - environment = cfg.settings; - script = '' - set -euo pipefail - umask u=rwx,g=,o= - - ${ - lib.optionalString (cfg.database.passwordFile != null) '' - INVENTREE_DB_PASSWORD=$(<"$CREDENTIALS_DIRECTORY/db_password") - '' - } \ - exec ${pkg}/bin/inventree migrate - ''; - }; - - systemd.services.inventree-static = { - description = "Inventree static migration"; - wantedBy = [ "inventree.target" ]; - partOf = [ "inventree.target" ]; - before = [ "inventree-server.service" ]; - environment = cfg.settings; - serviceConfig = { - User = cfg.user; - Group = cfg.group; - StateDirectory = "inventree"; - PrivateTmp = true; - } - // lib.optionalAttrs (cfg.database.passwordFile != null) { - LoadCredential = "db_password:${cfg.database.passwordFile}"; - }; - script = '' - ${ - lib.optionalString (cfg.database.passwordFile != null) '' - INVENTREE_DB_PASSWORD=$(<"$CREDENTIALS_DIRECTORY/db_password") - '' - } \ - exec ${pkg}/bin/inventree collectstatic --no-input - ''; - }; - - systemd.services.inventree-server = { - description = "Inventree Gunicorn service"; - requiredBy = [ "inventree.target" ]; - partOf = [ "inventree.target" ]; - environment = cfg.settings; - serviceConfig = { - User = cfg.user; - Group = cfg.group; - StateDirectory = "inventree"; - PrivateTmp = true; - } - // lib.optionalAttrs (cfg.database.passwordFile != null) { - LoadCredential = "db_password:${cfg.database.passwordFile}"; - }; - script = '' - ${ - lib.optionalString (cfg.database.passwordFile != null) '' - INVENTREE_DB_PASSWORD=$(<"$CREDENTIALS_DIRECTORY/db_password") - '' - } \ - exec ${pkg}/bin/gunicorn InvenTree.wsgi - ''; - }; - - systemd.sockets.inventree-server = { - wantedBy = [ "sockets.target" ]; - partOf = [ "inventree.target" ]; - socketConfig.ListenStream = "/run/inventree/gunicorn.socket"; - }; - - systemd.services.inventree-qcluster = { - description = "InvenTree qcluster server"; - requiredBy = [ "inventree.target" ]; - wantedBy = [ "inventree.target" ]; - partOf = [ "inventree.target" ]; - environment = cfg.settings; - serviceConfig = { - User = cfg.user; - Group = cfg.group; - StateDirectory = "inventree"; - PrivateTmp = true; - } - // lib.optionalAttrs (cfg.database.passwordFile != null) { - LoadCredential = "db_password:${cfg.database.passwordFile}"; - }; - script = '' - ${ - lib.optionalString (cfg.database.passwordFile != null) '' - INVENTREE_DB_PASSWORD=$(<"$CREDENTIALS_DIRECTORY/db_password") - '' - } \ - exec ${pkg}/bin/inventree qcluster - ''; - }; - - systemd.targets.inventree = { - description = "Target for all InvenTree services"; - wantedBy = [ "multi-user.target" ]; - wants = [ "network-online.target" ]; - after = [ "network-online.target" ]; - }; - - users = lib.optionalAttrs (cfg.user == cfg.user) { - users.${cfg.user} = { - group = cfg.group; - isSystemUser = true; - home = cfg.dataDir; - }; - groups.${cfg.group}.members = [ cfg.user ]; - }; - } - ] - ); -} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index d7863112185c..cd7d3b61325c 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -797,7 +797,6 @@ in installer = handleTest ./installer.nix { }; installer-systemd-stage-1 = handleTest ./installer-systemd-stage-1.nix { }; intune = runTest ./intune.nix; - inventree = runTest ./inventree.nix; invidious = runTest ./invidious.nix; invoiceplane = runTest ./invoiceplane.nix; iodine = runTest ./iodine.nix; diff --git a/nixos/tests/inventree.nix b/nixos/tests/inventree.nix deleted file mode 100644 index 34fe8730ae67..000000000000 --- a/nixos/tests/inventree.nix +++ /dev/null @@ -1,33 +0,0 @@ -{ lib, ... }: -{ - name = "inventree"; - meta.maintainers = with lib.maintainers; [ - kurogeek - ]; - - nodes = { - psqlTest = { - services.inventree = { - enable = true; - }; - }; - mysqlTest = { - services.inventree = { - enable = true; - database.dbtype = "mysql"; - }; - }; - }; - testScript = '' - start_all() - psqlTest.wait_for_unit("inventree.target") - psqlTest.wait_for_unit("inventree-server.service") - psqlTest.wait_for_open_unix_socket("/run/inventree/gunicorn.socket") - psqlTest.wait_until_succeeds("curl -sf http://localhost/web") - - mysqlTest.wait_for_unit("inventree.target") - mysqlTest.wait_for_unit("inventree-server.service") - mysqlTest.wait_for_open_unix_socket("/run/inventree/gunicorn.socket") - mysqlTest.wait_until_succeeds("curl -sf http://localhost/web") - ''; -} diff --git a/pkgs/by-name/in/inventree/package.nix b/pkgs/by-name/in/inventree/package.nix deleted file mode 100644 index 81b09762966a..000000000000 --- a/pkgs/by-name/in/inventree/package.nix +++ /dev/null @@ -1,291 +0,0 @@ -{ - fetchFromGitHub, - fetchYarnDeps, - lib, - nodejs, - python312, - stdenv, - yarnBuildHook, - yarnConfigHook, - yarnInstallHook, -}: -let - python3 = python312.override { - self = python3; - packageOverrides = self: super: { - django = super.django_4; - }; - }; - - version = "1.1.10"; - src = fetchFromGitHub { - owner = "inventree"; - repo = "inventree"; - tag = "${version}"; - hash = "sha256-TPB/3pFIU+ui4c+CbqIKTyAfJ/Xepm/RIhZeYhTrgI4="; - }; - - frontend = - let - frontendSource = src + "/src/frontend"; - in - stdenv.mkDerivation (finalAttrs: { - - pname = "inventree-frontend"; - inherit version; - - src = frontendSource; - - yarnOfflineCache = fetchYarnDeps { - yarnLock = finalAttrs.src + "/yarn.lock"; - hash = "sha256-Ijbkx+INZgsvMhkzo8h/FUY75W3UHnKAdUjQRD8kJZw="; - }; - - nativeBuildInputs = [ - nodejs - yarnConfigHook - yarnBuildHook - yarnInstallHook - ]; - - buildPhase = '' - mkdir -p $out - - export PATH=$PATH:$TMP/frontend/node_modules/.bin - substituteInPlace $TMP/frontend/vite.config.ts --replace-warn "../../src/backend/InvenTree/web/static/web" "$out/static/web" - - npm run extract - npm run compile - npm run build - ''; - }); -in -python3.pkgs.buildPythonApplication rec { - pname = "inventree"; - pyproject = true; - inherit version src; - - dependencies = - with python3.pkgs; - [ - django - dj-rest-auth - django-allauth - django-cleanup - django-cors-headers - django-crispy-forms - django-dbbackup - django-error-report-2 - django-filter - django-flags - django-formtools - django-ical - django-import-export - django-maintenance-mode - django-markdownify - django-money - django-mptt - django-mailbox - django-anymail - django-q2 - django-redis - django-sesame - django-sql-utils - django-sslserver - django-stdimage - django-storages - django-structlog - django-taggit - django-oauth-toolkit - django-otp - django-user-sessions - django-weasyprint - standard-imghdr - django-xforwardedfor-middleware - djangorestframework-simplejwt - djangorestframework - drf-spectacular - - bleach - cryptography - distutils - dulwich - feedparser - gunicorn - jinja2 - pdf2image - pillow - pint - python-barcode - python-dotenv - qrcode - pytz - pyyaml - rapidfuzz - sentry-sdk - structlog - tablib - tinycss2 - weasyprint - whitenoise - pypdf - ppf-datamatrix - psycopg2 - mysqlclient - requests-mock - - opentelemetry-api - opentelemetry-sdk - opentelemetry-exporter-otlp - opentelemetry-instrumentation-django - opentelemetry-instrumentation-requests - opentelemetry-instrumentation-redis - opentelemetry-instrumentation-sqlite3 - opentelemetry-instrumentation-system-metrics - opentelemetry-instrumentation-wsgi - ] - ++ tablib.optional-dependencies.all - ++ tablib.optional-dependencies.xls - ++ tablib.optional-dependencies.xlsx - ++ djangorestframework-simplejwt.optional-dependencies.crypto - ++ django-anymail.optional-dependencies.amazon-ses - ++ django-allauth.optional-dependencies.socialaccount - ++ django-allauth.optional-dependencies.saml - ++ django-allauth.optional-dependencies.openid - ++ django-allauth.optional-dependencies.mfa; - - build-system = [ python3.pkgs.setuptools ]; - - prePatch = - let - skippedCheckFunctions = [ - "test_task_check_for_updates" - "test_download_image" - "test_commit_info" - "test_rates" - "test_download_build_orders" - "test_valid_url" - "test_refresh_endpoint" - "test_download_csv" - "test_download_line_items" - "test_export" - "test_download_xlsx" - "test_download_csv" - "test_export" - "test_part_label_translation" - "test_part_download" - "test_date_filters" - "test_bom_export" - "test_hash" - "test_date" - "test_api_call" - "test_function_errors" - "test_stocktake_exporter" - "test_return" - "test_plugin_install" - "test_full_process" - "test_package_loading" - "test_export" - "test_users_exist" - "test_import_part" - "test_model_names" - ]; - skippedFuncScripts = builtins.map (funcName: '' - grep -rlZ ${funcName} . | while IFS= read -r -d "" file; do - substituteInPlace "$file" --replace-fail "${funcName}" "skip_${funcName}" - done - '') skippedCheckFunctions; - in - '' - ${lib.concatStringsSep "\n" skippedFuncScripts} - ''; - - installPhase = - let - pythonPath = python3.pkgs.makePythonPath dependencies; - in - '' - runHook preInstall - - # Don't need to bother with a non-maintained library from ages ago - substituteInPlace src/backend/InvenTree/InvenTree/settings.py --replace-fail "django_slowtests.testrunner.DiscoverSlowestTestsRunner" "django.test.runner.DiscoverRunner" - - mkdir -p $out/lib/${pname}/src/backend/InvenTree/web/ - cp -r src $out/lib/${pname} - ln -s ${frontend}/static $out/lib/${pname}/src/backend/InvenTree/web - # cp -r ${frontend}/static $out/lib/${pname}/src/backend/InvenTree/web - - chmod +x $out/lib/${pname}/src/backend/InvenTree/manage.py - - makeWrapper $out/lib/${pname}/src/backend/InvenTree/manage.py $out/bin/${pname} \ - --prefix PYTHONPATH : "${pythonPath}:$out/lib/${pname}/src/backend/InvenTree" - - makeWrapper ${lib.getExe python3.pkgs.gunicorn} $out/bin/gunicorn \ - --prefix PYTHONPATH : "${pythonPath}:$out/${python3.sitePackages}":"${pythonPath}:$out/lib/${pname}/src/backend/InvenTree" - - runHook postInstall - ''; - doCheck = true; - env = { - DJANGO_SETTINGS_MODULE = "InvenTree.settings"; - }; - - checkPhase = '' - runHook preCheck - - tmpDir=$(mktemp -d) - mkdir -p $tmpDir/media - mkdir -p $tmpDir/.cache/fontconfig - export HOME=$tmpDir - export INVENTREE_STATIC_ROOT=$tmpDir - export INVENTREE_MEDIA_ROOT=$tmpDir/media - export INVENTREE_BACKUP_DIR=$tmpDir - export INVENTREE_DB_ENGINE=django.db.backends.sqlite3 - export INVENTREE_DB_NAME=inventree.db - export INVENTREE_SITE_URL="http://localhost:8000" - - export INVENTREE_PLUGINS_ENABLED=true - export INVENTREE_PLUGIN_TESTING=true - export INVENTREE_PLUGIN_TESTING_SETUP=true - - pushd src/backend/InvenTree - ${python3.interpreter} ./manage.py check - ${python3.interpreter} ./manage.py migrate - - ${python3.interpreter} ./manage.py test --failfast - popd - - runHook postCheck - ''; - - nativeCheckInputs = with python3.pkgs; [ - django-test-migrations - pytest-django - pytest-env - pytestCheckHook - invoke - coverage - pytest-cov - pdfminer-six - ]; - passthru = - let - pythonPath = python3.pkgs.makePythonPath dependencies; - in - { - inherit frontend; - pythonPath = pythonPath; - }; - - meta = { - description = "Open Source Inventory Management System"; - homepage = "https://inventree.org/"; - changelog = "https://github.com/paperless-ngx/paperless-ngx/releases/tag/${src.tag}"; - license = lib.licenses.mit; - platforms = lib.platforms.linux; - mainProgram = "inventree"; - maintainers = with lib.maintainers; [ - kurogeek - ]; - }; -}