at 25.11-pre 4.0 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7 8let 9 cfg = config.services.scanservjs; 10 settings = { 11 scanimage = lib.getExe' config.hardware.sane.backends-package "scanimage"; 12 convert = lib.getExe' pkgs.imagemagick "convert"; 13 tesseract = lib.getExe pkgs.tesseract; 14 # it defaults to config/devices.json, but "config" dir doesn't exist by default and scanservjs doesn't create it 15 devicesPath = "devices.json"; 16 } // cfg.settings; 17 settingsFormat = pkgs.formats.json { }; 18 19 leafs = 20 attrs: 21 builtins.concatLists ( 22 lib.mapAttrsToList (k: v: if builtins.isAttrs v then leafs v else [ v ]) attrs 23 ); 24 25 package = pkgs.scanservjs; 26 27 configFile = pkgs.writeText "config.local.js" '' 28 /* eslint-disable no-unused-vars */ 29 module.exports = { 30 afterConfig(config) { 31 ${builtins.concatStringsSep "" ( 32 leafs ( 33 lib.mapAttrsRecursive (path: val: '' 34 ${builtins.concatStringsSep "." path} = ${builtins.toJSON val}; 35 '') { config = settings; } 36 ) 37 )} 38 ${cfg.extraConfig} 39 }, 40 41 afterDevices(devices) { 42 ${cfg.extraDevicesConfig} 43 }, 44 45 async afterScan(fileInfo) { 46 ${cfg.runAfterScan} 47 }, 48 49 actions: [ 50 ${builtins.concatStringsSep ",\n" cfg.extraActions} 51 ], 52 }; 53 ''; 54 55in 56{ 57 options.services.scanservjs = { 58 enable = lib.mkEnableOption "scanservjs"; 59 stateDir = lib.mkOption { 60 type = lib.types.str; 61 default = "/var/lib/scanservjs"; 62 description = '' 63 State directory for scanservjs. 64 ''; 65 }; 66 settings = lib.mkOption { 67 default = { }; 68 description = '' 69 Config to set in config.local.js's `afterConfig`. 70 ''; 71 type = lib.types.submodule { 72 freeformType = settingsFormat.type; 73 options.host = lib.mkOption { 74 type = lib.types.str; 75 description = "The IP to listen on."; 76 default = "127.0.0.1"; 77 }; 78 options.port = lib.mkOption { 79 type = lib.types.port; 80 description = "The port to listen on."; 81 default = 8080; 82 }; 83 }; 84 }; 85 extraConfig = lib.mkOption { 86 default = ""; 87 type = lib.types.lines; 88 description = '' 89 Extra code to add to config.local.js's `afterConfig`. 90 ''; 91 }; 92 extraDevicesConfig = lib.mkOption { 93 default = ""; 94 type = lib.types.lines; 95 description = '' 96 Extra code to add to config.local.js's `afterDevices`. 97 ''; 98 }; 99 runAfterScan = lib.mkOption { 100 default = ""; 101 type = lib.types.lines; 102 description = '' 103 Extra code to add to config.local.js's `afterScan`. 104 ''; 105 }; 106 extraActions = lib.mkOption { 107 default = [ ]; 108 type = lib.types.listOf lib.types.lines; 109 description = "Actions to add to config.local.js's `actions`."; 110 }; 111 }; 112 113 config = lib.mkIf cfg.enable { 114 hardware.sane.enable = true; 115 users.users.scanservjs = { 116 group = "scanservjs"; 117 extraGroups = [ 118 "scanner" 119 "lp" 120 ]; 121 home = cfg.stateDir; 122 isSystemUser = true; 123 createHome = true; 124 }; 125 users.groups.scanservjs = { }; 126 127 systemd.services.scanservjs = { 128 description = "scanservjs"; 129 after = [ "network.target" ]; 130 wantedBy = [ "multi-user.target" ]; 131 # yes, those paths are configurable, but the config option isn't always used... 132 # a lot of the time scanservjs just takes those from PATH 133 path = with pkgs; [ 134 coreutils 135 config.hardware.sane.backends-package 136 imagemagick 137 tesseract 138 ]; 139 environment = { 140 NIX_SCANSERVJS_CONFIG_PATH = configFile; 141 SANE_CONFIG_DIR = "/etc/sane-config"; 142 LD_LIBRARY_PATH = "/etc/sane-libs"; 143 }; 144 serviceConfig = { 145 ExecStart = lib.getExe package; 146 Restart = "always"; 147 User = "scanservjs"; 148 Group = "scanservjs"; 149 WorkingDirectory = cfg.stateDir; 150 }; 151 }; 152 }; 153}