{ config, pkgs, inputs, ... }: let # The application lives next to the annotation tooling in the eReader # workspace and arrives here as a locked flake input. Copy it once more to # a normal store path because a non-flake path input can retain its relative # spelling during evaluation. The running service never executes from a # mutable home directory. viewer = builtins.path { path = inputs.ereaderViewer; name = "ereader-viewer-source"; }; host = "ereader.${config.domain2}"; port = 8081; in { systemd.services.ereader-viewer = { description = "Read-only KOReader annotation viewer"; wantedBy = [ "multi-user.target" ]; after = [ "local-fs.target" ]; path = [ pkgs.unzip pkgs."poppler-utils" ]; serviceConfig = { ExecStart = "${pkgs.nodejs}/bin/node ${viewer}/server.mjs"; WorkingDirectory = viewer; User = "drone"; Group = "users"; Environment = [ "ANNOTATIONS_PATH=/srv/ereader/library/annotations.json" "BOOKS_PATH=/srv/ereader/library/books" "HOST=127.0.0.1" "PORT=${toString port}" ]; Restart = "always"; RestartSec = "5s"; NoNewPrivileges = true; PrivateTmp = true; PrivateDevices = true; ProtectHome = true; ProtectSystem = "strict"; ProtectKernelTunables = true; ProtectKernelModules = true; ProtectControlGroups = true; RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ]; }; }; services.traefik.dynamicConfigOptions.http = { routers.ereader-viewer = { rule = "Host(`${host}`)"; entryPoints = [ "web-internal" ]; service = "ereader-viewer"; tls = { }; }; services.ereader-viewer.loadBalancer.servers = [ { url = "http://127.0.0.1:${toString port}"; } ]; }; }