diff --git a/nixos/doc/manual/release-notes/rl-2505.section.md b/nixos/doc/manual/release-notes/rl-2505.section.md index 25468f300456..5eb35f0ab746 100644 --- a/nixos/doc/manual/release-notes/rl-2505.section.md +++ b/nixos/doc/manual/release-notes/rl-2505.section.md @@ -14,6 +14,8 @@ - [Bazecor](https://github.com/Dygmalab/Bazecor), the graphical configurator for Dygma Products. +- [scanservjs](https://github.com/sbs20/scanservjs/), a web UI for SANE scanners. Available at [services.scanservjs](#opt-services.scanservjs.enable). + - [Kimai](https://www.kimai.org/), a web-based multi-user time-tracking application. Available as [services.kimai](options.html#opt-services.kimai). - [Omnom](https://github.com/asciimoo/omnom), a webpage bookmarking and snapshotting service. Available as [services.omnom](options.html#opt-services.omnom.enable). diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index a07d979a07fa..94b8cc51cd89 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -621,6 +621,7 @@ ./services/hardware/sane_extra_backends/brscan4.nix ./services/hardware/sane_extra_backends/brscan5.nix ./services/hardware/sane_extra_backends/dsseries.nix + ./services/hardware/scanservjs.nix ./services/hardware/spacenavd.nix ./services/hardware/supergfxd.nix ./services/hardware/tcsd.nix diff --git a/nixos/modules/services/hardware/scanservjs.nix b/nixos/modules/services/hardware/scanservjs.nix new file mode 100644 index 000000000000..7f361767c499 --- /dev/null +++ b/nixos/modules/services/hardware/scanservjs.nix @@ -0,0 +1,155 @@ +{ + config, + lib, + pkgs, + ... +}: + +let + cfg = config.services.scanservjs; + settings = { + scanimage = lib.getExe' config.hardware.sane.backends-package "scanimage"; + convert = lib.getExe' pkgs.imagemagick "convert"; + tesseract = lib.getExe pkgs.tesseract; + # it defaults to config/devices.json, but "config" dir doesn't exist by default and scanservjs doesn't create it + devicesPath = "devices.json"; + } // cfg.settings; + settingsFormat = pkgs.formats.json { }; + + leafs = + attrs: + builtins.concatLists ( + lib.mapAttrsToList (k: v: if builtins.isAttrs v then leafs v else [ v ]) attrs + ); + + package = pkgs.scanservjs; + + configFile = pkgs.writeText "config.local.js" '' + /* eslint-disable no-unused-vars */ + module.exports = { + afterConfig(config) { + ${ + builtins.concatStringsSep "" ( + leafs ( + lib.mapAttrsRecursive (path: val: '' + ${builtins.concatStringsSep "." path} = ${builtins.toJSON val}; + '') { config = settings; } + ) + ) + } + ${cfg.extraConfig} + }, + + afterDevices(devices) { + ${cfg.extraDevicesConfig} + }, + + async afterScan(fileInfo) { + ${cfg.runAfterScan} + }, + + actions: [ + ${builtins.concatStringsSep ",\n" cfg.extraActions} + ], + }; + ''; + +in +{ + options.services.scanservjs = { + enable = lib.mkEnableOption "scanservjs"; + stateDir = lib.mkOption { + type = lib.types.str; + default = "/var/lib/scanservjs"; + description = '' + State directory for scanservjs. + ''; + }; + settings = lib.mkOption { + default = { }; + description = '' + Config to set in config.local.js's `afterConfig`. + ''; + type = lib.types.submodule { + freeformType = settingsFormat.type; + options.host = lib.mkOption { + type = lib.types.str; + description = "The IP to listen on."; + default = "127.0.0.1"; + }; + options.port = lib.mkOption { + type = lib.types.port; + description = "The port to listen on."; + default = 8080; + }; + }; + }; + extraConfig = lib.mkOption { + default = ""; + type = lib.types.lines; + description = '' + Extra code to add to config.local.js's `afterConfig`. + ''; + }; + extraDevicesConfig = lib.mkOption { + default = ""; + type = lib.types.lines; + description = '' + Extra code to add to config.local.js's `afterDevices`. + ''; + }; + runAfterScan = lib.mkOption { + default = ""; + type = lib.types.lines; + description = '' + Extra code to add to config.local.js's `afterScan`. + ''; + }; + extraActions = lib.mkOption { + default = [ ]; + type = lib.types.listOf lib.types.lines; + description = "Actions to add to config.local.js's `actions`."; + }; + }; + + config = lib.mkIf cfg.enable { + hardware.sane.enable = true; + users.users.scanservjs = { + group = "scanservjs"; + extraGroups = [ + "scanner" + "lp" + ]; + home = cfg.stateDir; + isSystemUser = true; + createHome = true; + }; + users.groups.scanservjs = { }; + + systemd.services.scanservjs = { + description = "scanservjs"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + # yes, those paths are configurable, but the config option isn't always used... + # a lot of the time scanservjs just takes those from PATH + path = with pkgs; [ + coreutils + config.hardware.sane.backends-package + imagemagick + tesseract + ]; + environment = { + NIX_SCANSERVJS_CONFIG_PATH = configFile; + SANE_CONFIG_DIR = "/etc/sane-config"; + LD_LIBRARY_PATH = "/etc/sane-libs"; + }; + serviceConfig = { + ExecStart = lib.getExe package; + Restart = "always"; + User = "scanservjs"; + Group = "scanservjs"; + WorkingDirectory = cfg.stateDir; + }; + }; + }; +}