Skip to content

Commit

Permalink
nixos/froide: init
Browse files Browse the repository at this point in the history
  • Loading branch information
onny committed Nov 24, 2024
1 parent cf47996 commit 96bb1f3
Show file tree
Hide file tree
Showing 4 changed files with 238 additions and 0 deletions.
2 changes: 2 additions & 0 deletions nixos/doc/manual/release-notes/rl-2505.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

- [Kimai](https://www.kimai.org/), a web-based multi-user time-tracking application. Available as [services.kimai](option.html#opt-services.kimai).

- [Froide](https://github.com/okfde/froide), a freedom of information act web application. Available as [services.froide](#opt-services.froide.enable).

- [Amazon CloudWatch Agent](https://github.com/aws/amazon-cloudwatch-agent), the official telemetry collector for AWS CloudWatch and AWS X-Ray. Available as [services.amazon-cloudwatch-agent](#opt-services.amazon-cloudwatch-agent.enable).

<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
Expand Down
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
145 changes: 145 additions & 0 deletions nixos/modules/services/web-apps/froide.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
{
config,
lib,
pkgs,
...
}:
let

cfg = config.services.froide;
format = pkgs.formats.toml { };

in
{
options.services.froide = {

enable = lib.mkEnableOption "Freedom of information portal web app Froide";

package = lib.mkPackageOption pkgs "froide" { };

port = lib.mkOption {
type = with lib.types; nullOr port;
default = 8001;
description = "Port to listen on.";
};

dataDir = lib.mkOption {
type = lib.types.str;
default = "/var/lib/froide";
description = "Directory to store the Froide server data.";
};

openFirewall = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Whether to open ports in the firewall for the server.
'';
};

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 = "[email protected]";
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 = "${lib.getExe cfg.package} runserver 0.0.0.0:8000";
WorkingDirectory = cfg.dataDir;
StateDirectory = [ cfg.dataDir ];
DynamicUser = true;
EnvironmentFile = [
(format.generate "froide-env" cfg.settings)
cfg.secrets
];
Environment = [ "DJANGO_CONFIGURATION=Production" ];
};
after = [ "postgresql.service" ];
wantedBy = [ "multi-user.target" ];
preStart = ''
# Auto-migrate on first run or if the package has changed
versionFile="${cfg.dataDir}/src-version"
if [[ $(cat "$versionFile" 2>/dev/null) != ${cfg.package} ]]; then
etebase-server migrate --no-input
etebase-server collectstatic --no-input --clear
echo ${cfg.package} > "$versionFile"
fi
# FIXME adapt paperless-ngx check for new versions
${lib.getExe cfg.package} collectstatic
${lib.getExe cfg.package} migrate
'';
};
};

environment.systemPackages = [ pkgs.froide ];

networking.firewall = lib.mkIf cfg.openFirewall {
allowedTCPPorts = [ cfg.port ];
};

};

meta.maintainers = with lib.maintainers; [ onny ];

}
90 changes: 90 additions & 0 deletions nixos/tests/froide.nix
Original file line number Diff line number Diff line change
@@ -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)
'';
})

0 comments on commit 96bb1f3

Please sign in to comment.