From 61161c1ee2d52e777bc8588e8ae6bc9cd9ffcdae Mon Sep 17 00:00:00 2001 From: Jonas Heinrich Date: Wed, 13 Nov 2024 08:41:00 +0100 Subject: [PATCH] nixos/froide: init --- .../manual/release-notes/rl-2411.section.md | 2 + nixos/modules/module-list.nix | 1 + nixos/modules/services/web-apps/froide.nix | 111 ++++++++++++++++++ nixos/tests/froide.nix | 90 ++++++++++++++ 4 files changed, 204 insertions(+) create mode 100644 nixos/modules/services/web-apps/froide.nix create mode 100644 nixos/tests/froide.nix diff --git a/nixos/doc/manual/release-notes/rl-2411.section.md b/nixos/doc/manual/release-notes/rl-2411.section.md index 95d7e5f907f7d..72e9a16a26d39 100644 --- a/nixos/doc/manual/release-notes/rl-2411.section.md +++ b/nixos/doc/manual/release-notes/rl-2411.section.md @@ -119,6 +119,8 @@ - [Soteria](https://github.com/ImVaskel/soteria), a polkit authentication agent to handle elevated prompts for any desktop environment. Normally this should only be used on DEs or WMs that do not provide a graphical polkit frontend on their own. Available as [`security.soteria`](#opt-security.soteria.enable). +- [Froide](https://github.com/okfde/froide), a freedom of information act web application. Available as [services.froide](#opt-services.froide.enable). + - [Flood](https://flood.js.org/), a beautiful WebUI for various torrent clients. Available as [services.flood](options.html#opt-services.flood.enable). - [Niri](https://github.com/YaLTeR/niri), a scrollable-tiling Wayland compositor. Available as [programs.niri](options.html#opt-programs.niri.enable). diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 5cadc0a5b0323..5e97a5f25007e 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1437,6 +1437,7 @@ ./services/web-apps/flarum.nix ./services/web-apps/fluidd.nix ./services/web-apps/freshrss.nix + ./services/web-apps/froide.nix ./services/web-apps/galene.nix ./services/web-apps/gancio.nix ./services/web-apps/gerrit.nix diff --git a/nixos/modules/services/web-apps/froide.nix b/nixos/modules/services/web-apps/froide.nix new file mode 100644 index 0000000000000..72dfcca95ba67 --- /dev/null +++ b/nixos/modules/services/web-apps/froide.nix @@ -0,0 +1,111 @@ +{ + config, + lib, + pkgs, + ... +}: +let + + cfg = config.services.froide; + format = pkgs.formats.toml { }; + +in +{ + options.services.froide = { + + enable = lib.mkEnableOption "Gouvernment planer web app Govplan"; + + settings = lib.mkOption { + default = { }; + description = '' + IMAP authentication configuration for rspamd-trainer. For supplying + the IMAP password, use the `secrets` option. + ''; + type = lib.types.submodule { freeformType = format.type; }; + example = lib.literalExpression '' + { + HOST = "localhost"; + USERNAME = "spam@example.com"; + INBOXPREFIX = "INBOX/"; + } + ''; + }; + + secrets = lib.mkOption { + type = with lib.types; listOf path; + description = '' + A list of files containing the various secrets. Should be in the + format expected by systemd's `EnvironmentFile` directory. For the + IMAP account password use `PASSWORD = mypassword`. + ''; + default = [ ]; + }; + + }; + + config = lib.mkIf cfg.enable { + + services.postgresql = { + enable = true; + ensureDatabases = [ "froide" ]; + ensureUsers = [ + { + name = "froide"; + ensureDBOwnership = true; + } + ]; + extraPlugins = ps: with ps; [ postgis ]; + authentication = '' + host froide froide localhost trust + ''; + initialScript = pkgs.writeText "backend-initScript" '' + ALTER USER froide WITH SUPERUSER; + ''; + }; + + systemd.services = { + + postgresql.serviceConfig.ExecStartPost = + let + sqlFile = pkgs.writeText "froide-pgvectors-setup.sql" '' + ALTER USER froide WITH SUPERUSER; + #CREATE EXTENSION IF NOT EXISTS postgis; + #ALTER SCHEMA govplan OWNER TO govplan; + #ALTER EXTENSION govplan UPDATE; + ''; + in + [ + '' + ${lib.getExe' config.services.postgresql.package "psql"} -d froide -f "${sqlFile}" + '' + ]; + + froide = { + description = "Gouvernment planer Govplan"; + serviceConfig = { + ExecStart = "${pkgs.froide}/bin/froide runserver 0.0.0.0:8000"; + WorkingDirectory = "/var/lib/froide"; + StateDirectory = [ "froide" ]; + DynamicUser = true; + EnvironmentFile = [ + (format.generate "froide-env" cfg.settings) + cfg.secrets + ]; + Environment = [ "DJANGO_CONFIGURATION=Production" ]; + }; + after = [ "postgresql.service" ]; + wantedBy = [ "multi-user.target" ]; + preStart = '' + # FIXME adapt paperless-ngx check for new versions + ${pkgs.froide}/bin/froide migrate + ''; + }; + }; + + environment.systemPackages = [ pkgs.froide ]; + + }; + + meta.maintainers = with lib.maintainers; [ onny ]; + +} diff --git a/nixos/tests/froide.nix b/nixos/tests/froide.nix new file mode 100644 index 0000000000000..c895780669140 --- /dev/null +++ b/nixos/tests/froide.nix @@ -0,0 +1,90 @@ +import ./make-test-python.nix ({ lib, ... }: { + name = "paperless"; + meta.maintainers = with lib.maintainers; [ leona SuperSandro2000 erikarvstedt ]; + + nodes = let self = { + simple = { pkgs, ... }: { + environment.systemPackages = with pkgs; [ imagemagick jq ]; + services.paperless = { + enable = true; + passwordFile = builtins.toFile "password" "admin"; + }; + }; + postgres = { config, pkgs, ... }: { + imports = [ self.simple ]; + services.postgresql = { + enable = true; + ensureDatabases = [ "paperless" ]; + ensureUsers = [ + { name = config.services.paperless.user; + ensureDBOwnership = true; + } + ]; + }; + services.paperless.settings = { + PAPERLESS_DBHOST = "/run/postgresql"; + PAPERLESS_OCR_LANGUAGE = "deu"; + }; + }; + }; in self; + + testScript = '' + import json + + def test_paperless(node): + node.wait_for_unit("paperless-consumer.service") + + with subtest("Add a document via the file system"): + node.succeed( + "convert -size 400x40 xc:white -font 'DejaVu-Sans' -pointsize 20 -fill black " + "-annotate +5+20 'hello world 16-10-2005' /var/lib/paperless/consume/doc.png" + ) + + with subtest("Web interface gets ready"): + node.wait_for_unit("paperless-web.service") + # Wait until server accepts connections + node.wait_until_succeeds("curl -fs localhost:28981") + + # Required for consuming documents via the web interface + with subtest("Task-queue gets ready"): + node.wait_for_unit("paperless-task-queue.service") + + with subtest("Add a png document via the web interface"): + node.succeed( + "convert -size 400x40 xc:white -font 'DejaVu-Sans' -pointsize 20 -fill black " + "-annotate +5+20 'hello web 16-10-2005' /tmp/webdoc.png" + ) + node.wait_until_succeeds("curl -u admin:admin -F document=@/tmp/webdoc.png -fs localhost:28981/api/documents/post_document/") + + with subtest("Add a txt document via the web interface"): + node.succeed( + "echo 'hello web 16-10-2005' > /tmp/webdoc.txt" + ) + node.wait_until_succeeds("curl -u admin:admin -F document=@/tmp/webdoc.txt -fs localhost:28981/api/documents/post_document/") + + with subtest("Documents are consumed"): + node.wait_until_succeeds( + "(($(curl -u admin:admin -fs localhost:28981/api/documents/ | jq .count) == 3))" + ) + docs = json.loads(node.succeed("curl -u admin:admin -fs localhost:28981/api/documents/"))['results'] + assert "2005-10-16" in docs[0]['created'] + assert "2005-10-16" in docs[1]['created'] + assert "2005-10-16" in docs[2]['created'] + + # Detects gunicorn issues, see PR #190888 + with subtest("Document metadata can be accessed"): + metadata = json.loads(node.succeed("curl -u admin:admin -fs localhost:28981/api/documents/1/metadata/")) + assert "original_checksum" in metadata + + metadata = json.loads(node.succeed("curl -u admin:admin -fs localhost:28981/api/documents/2/metadata/")) + assert "original_checksum" in metadata + + metadata = json.loads(node.succeed("curl -u admin:admin -fs localhost:28981/api/documents/3/metadata/")) + assert "original_checksum" in metadata + + test_paperless(simple) + simple.send_monitor_command("quit") + simple.wait_for_shutdown() + test_paperless(postgres) + ''; +})