diff --git a/flake.nix b/flake.nix index f794319..99fc061 100644 --- a/flake.nix +++ b/flake.nix @@ -131,6 +131,7 @@ nixos-mailserver.nixosModules.default ./system/devices/hp-probook-g0-server.nix (if devMaubot then import /${devPath}/maubot.nix/module else maubot.nixosModules.default) + ./system/modules/scanservjs.nix ]; }; nixserver-cross = crossConfig nixserver; diff --git a/home/common/nvim.nix b/home/common/nvim.nix index 86991a4..86870b8 100644 --- a/home/common/nvim.nix +++ b/home/common/nvim.nix @@ -178,9 +178,8 @@ ]); plugins = let ps = pkgs.vimPlugins; in map (x: if x?config && x?plugin then { type = "lua"; } // x else x) [ ps.vim-svelte - # TODO remove on next nvim update (0.8.3/0.9? whenever they add builtin nix syntax) - # testing the removal - # ps.vim-nix + # vim-nix isn't necessary for syntax highlighting, but it improves overall editing experience + ps.vim-nix { plugin = pkgs.vimUtils.buildVimPluginFrom2Nix { pname = "vscode-nvim"; version = "2023-02-10"; diff --git a/pkgs/default.nix b/pkgs/default.nix index cf0df6d..79c3583 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -5,7 +5,7 @@ , pkgs' ? pkgs , ... }: let - inherit (pkgs) callPackage; + inherit (pkgs') callPackage; sources = import ./_sources/generated.nix { inherit (pkgs) fetchgit fetchurl fetchFromGitHub dockerTools; }; @@ -13,21 +13,6 @@ let in { - linux-firmware = if pkgs.system == "x86_64-linux" then pkgs.stdenvNoCC.mkDerivation { - inherit (pkgs.linux-firmware) pname version src meta; - dontFixup = true; - passthru = { inherit (pkgs.linux-firmware) version; }; - installFlags = [ "DESTDIR=$(out)" ]; - - # revert microcode updates which break boot for me - patches = [ ./revert-amd-ucode-update.patch ]; - postPatch = '' - cp ${pkgs.fetchurl { - url = "https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/plain/amd-ucode/microcode_amd_fam17h.bin?id=b250b32ab1d044953af2dc5e790819a7703b7ee6"; - hash = "sha256-HnKjEb2di7BiKB09JYUjIUuZNCVgXlwRSbjijnuYBcM="; - }} amd-ucode/microcode_amd_fam17h.bin - ''; - } else pkgs.linux-firmware; inherit (nix-gaming) faf-client osu-lazer-bin; inherit nixForNixPlugins; nix = nixForNixPlugins; @@ -100,6 +85,7 @@ in ''; }; rofi-steam-game-list = callPackage ./rofi-steam-game-list { }; + scanservjs = callPackage ./scanservjs.nix { }; searxng = pkgs'.python3.pkgs.toPythonModule (pkgs.searxng.overrideAttrs (old: { inherit (sources.searxng) src; version = "unstable-" + sources.searxng.date; diff --git a/pkgs/scanservjs.nix b/pkgs/scanservjs.nix new file mode 100644 index 0000000..04ccb75 --- /dev/null +++ b/pkgs/scanservjs.nix @@ -0,0 +1,91 @@ +{ lib +, fetchFromGitHub +, buildNpmPackage +, fetchNpmDeps +, nodejs +}: + +let + version = "2.27.0"; + src = fetchFromGitHub { + owner = "sbs20"; + repo = "scanservjs"; + rev = "v${version}"; + hash = "sha256-GFpfH7YSXFRNRmx8F2bUJsGdPW1ECT7AQquJRxiRJEU="; + }; + + depsHashes = { + server = "sha256-V4w4euMl67eS4WNIFM8j06/JAEudaq+4zY9pFVgTmlY="; + client = "sha256-r/uYaXpQnlI90Yn6mo2KViKDMHE8zaCAxNFnEZslnaY="; + }; + + serverDepsForClient = fetchNpmDeps { + inherit src nodejs; + sourceRoot = "${src.name}/packages/server"; + name = "scanservjs-server"; + hash = depsHashes.server or lib.fakeHash; + }; + + # static client files + client = buildNpmPackage ({ + pname = "scanservjs-static"; + inherit version src nodejs; + + sourceRoot = "${src.name}/packages/client"; + npmDepsHash = depsHashes.client or lib.fakeHash; + + preBuild = '' + cd ../server + chmod +w package-lock.json . /build/source/ + npmDeps=${serverDepsForClient} npmConfigHook + cd ../client + ''; + + env.NODE_OPTIONS = "--openssl-legacy-provider"; + + dontNpmInstall = true; + installPhase = '' + mv /build/source/dist/client $out + ''; + }); + +in buildNpmPackage { + pname = "scanservjs"; + inherit version src nodejs; + + sourceRoot = "${src.name}/packages/server"; + npmDepsHash = depsHashes.server or lib.fakeHash; + + preBuild = '' + chmod +w /build/source + substituteInPlace src/server.js --replace "express.static('client')" "express.static('${client}')" + substituteInPlace src/api.js --replace \ + '`''${config.previewDirectory}/default.jpg`' \ + "'$out/lib/node_modules/scanservjs-api/data/preview/default.jpg'" + substituteInPlace src/application.js --replace \ + "'../../config/config.local.js'" \ + "process.env.NIX_SCANSERVJS_CONFIG_PATH" + substituteInPlace src/classes/user-options.js --replace \ + "const localPath = path.join(__dirname, localConfigPath);" \ + "const localPath = localConfigPath;" + substituteInPlace src/configure.js --replace \ + "fs.mkdirSync(config.outputDirectory, { recursive: true });" \ + "fs.mkdirSync(config.outputDirectory, { recursive: true }); fs.mkdirSync(config.previewDirectory, { recursive: true });" + ''; + + postInstall = '' + mkdir -p $out/bin + makeWrapper ${nodejs}/bin/node $out/bin/scanservjs \ + --set NODE_ENV production \ + --add-flags "'$out/lib/node_modules/scanservjs-api/src/server.js'" + ''; + + meta = with lib; { + description = "SANE scanner nodejs web ui"; + longDescription = "scanservjs is a simple web-based UI for SANE which allows you to share a scanner on a network without the need for drivers or complicated installation."; + homepage = "https://github.com/sbs20/scanservjs"; + license = licenses.gpl2Only; + mainProgram = "scanservjs"; + maintainers = with maintainers; [ chayleaf ]; + }; +} diff --git a/system/devices/msi-delta-15-workstation.nix b/system/devices/msi-delta-15-workstation.nix index 9493010..3647511 100644 --- a/system/devices/msi-delta-15-workstation.nix +++ b/system/devices/msi-delta-15-workstation.nix @@ -11,7 +11,7 @@ let dataroot = "/dev/disk/by-uuid/${datarootUuid}"; in { imports = [ - ../hardware/msi-delta-15.nix + ../hardware/msi-delta-15 ../hosts/nixmsi.nix ]; diff --git a/system/hardware/msi-delta-15.nix b/system/hardware/msi-delta-15.nix deleted file mode 100644 index 22148d1..0000000 --- a/system/hardware/msi-delta-15.nix +++ /dev/null @@ -1,24 +0,0 @@ -{ hardware -, ... }: - -{ - imports = with hardware; [ - common-pc-ssd # enables fstrim - common-cpu-amd # microcode - common-cpu-amd-pstate # amd-pstate - common-gpu-amd # configures drivers - common-pc-laptop # enables tlp - ]; - common.resolution = "1920x1080"; - vfio.pciIDs = [ "1002:73df" "1002:ab28" ]; - boot = { - initrd.availableKernelModules = [ "nvme" "xhci_pci" ]; - kernelParams = [ - # disable PSR to *hopefully* avoid random hangs - # this one didnt help - "amdgpu.dcdebugmask=0x10" - # maybe this one will? - "amdgpu.noretry=0" - ]; - }; -} diff --git a/system/hardware/msi-delta-15/default.nix b/system/hardware/msi-delta-15/default.nix new file mode 100644 index 0000000..0d937d4 --- /dev/null +++ b/system/hardware/msi-delta-15/default.nix @@ -0,0 +1,54 @@ +{ hardware +, ... }: + +{ + imports = with hardware; [ + common-pc-ssd # enables fstrim + common-cpu-amd # microcode + common-cpu-amd-pstate # amd-pstate + common-gpu-amd # configures drivers + common-pc-laptop # enables tlp + ]; + common.resolution = "1920x1080"; + vfio.pciIDs = [ "1002:73df" "1002:ab28" ]; + boot = { + initrd.availableKernelModules = [ "nvme" "xhci_pci" ]; + kernelParams = [ + # disable PSR to *hopefully* avoid random hangs + # this one didnt help + "amdgpu.dcdebugmask=0x10" + # maybe this one will? + "amdgpu.noretry=0" + ]; + }; + # TODO: really, really, please, I want latest firmware to work... + nixpkgs.overlays = [ + (final: prev: { + amd-ucode = prev.amd-ucode.override { inherit (final) linux-firmware; }; + linux-firmware = prev.stdenvNoCC.mkDerivation { + inherit (prev.linux-firmware) pname version meta src; + dontFixup = true; + passthru = { inherit (prev.linux-firmware) version; }; + installFlags = [ "DESTDIR=$(out)" ]; + + # revert microcode updates which break boot for me + patches = [ + ./revert-amd-ucode-update-fam17h.patch + ./revert-amd-ucode-update-fam19h.patch + ]; + postPatch = '' + cp ${final.fetchurl { + name = "microcode_amd_fam17h.bin"; + url = "https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/plain/amd-ucode/microcode_amd_fam17h.bin?id=b250b32ab1d044953af2dc5e790819a7703b7ee6"; + hash = "sha256-HnKjEb2di7BiKB09JYUjIUuZNCVgXlwRSbjijnuYBcM="; + }} amd-ucode/microcode_amd_fam17h.bin + cp ${final.fetchurl { + name = "microcode_amd_fam19h.bin"; + url = "https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/plain/amd-ucode/microcode_amd_fam19h.bin?id=0ab353f8d8aa96d68690911cea22ec538f3095c4"; + hash = "sha256-LlA+E4EVQpfjD3/cg6Y52BsCGW/5ZfY0J2UnCUI/3MQ"; + }} amd-ucode/microcode_amd_fam19h.bin + ''; + }; + }) + ]; +} diff --git a/pkgs/revert-amd-ucode-update.patch b/system/hardware/msi-delta-15/revert-amd-ucode-update-fam17h.patch similarity index 100% rename from pkgs/revert-amd-ucode-update.patch rename to system/hardware/msi-delta-15/revert-amd-ucode-update-fam17h.patch diff --git a/system/hardware/msi-delta-15/revert-amd-ucode-update-fam19h.patch b/system/hardware/msi-delta-15/revert-amd-ucode-update-fam19h.patch new file mode 100644 index 0000000..3643dbb --- /dev/null +++ b/system/hardware/msi-delta-15/revert-amd-ucode-update-fam19h.patch @@ -0,0 +1,64 @@ +diff --git a/WHENCE b/WHENCE +index 54aadb0..99cee97 100644 +--- a/WHENCE ++++ b/WHENCE +@@ -3924,7 +3924,7 @@ Raw: amd-ucode/microcode_amd_fam17h.bin + Version: 2023-07-19 + File: amd-ucode/microcode_amd_fam19h.bin + Raw: amd-ucode/microcode_amd_fam19h.bin +-Version: 2023-08-08 ++Version: 2023-07-18 + File: amd-ucode/README + + License: Redistributable. See LICENSE.amd-ucode for details +diff --git a/amd-ucode/README b/amd-ucode/README +index fac1152..1d39da3 100644 +--- a/amd-ucode/README ++++ b/amd-ucode/README +@@ -37,19 +37,6 @@ Microcode patches in microcode_amd_fam17h.bin: + Family=0x17 Model=0x01 Stepping=0x02: Patch=0x0800126e Length=3200 bytes + + Microcode patches in microcode_amd_fam19h.bin: +- Family=0x19 Model=0x11 Stepping=0x01: Patch=0x0a10113e Length=5568 bytes +- Family=0x19 Model=0x11 Stepping=0x02: Patch=0x0a10123e Length=5568 bytes +- Family=0x19 Model=0xa0 Stepping=0x02: Patch=0x0aa00212 Length=5568 bytes + Family=0x19 Model=0x01 Stepping=0x01: Patch=0x0a0011d1 Length=5568 bytes + Family=0x19 Model=0x01 Stepping=0x00: Patch=0x0a001079 Length=5568 bytes + Family=0x19 Model=0x01 Stepping=0x02: Patch=0x0a001234 Length=5568 bytes +- Family=0x19 Model=0xa0 Stepping=0x01: Patch=0x0aa00116 Length=5568 bytes +- +-NOTE: For Genoa (Family=0x19 Model=0x11) and Bergamo (Family=0x19 Model=0xa0), +-either AGESA version >= 1.0.0.8 OR a kernel with the following commit is +-required: +-a32b0f0db3f3 ("x86/microcode/AMD: Load late on both threads too") +- +-When late loading the patches for Genoa or Bergamo, there may be one spurious +-NMI observed per physical core. These NMIs are benign and don't cause any +-functional issue but will result in kernel messages being logged. +diff --git a/amd-ucode/microcode_amd_fam19h.bin b/amd-ucode/microcode_amd_fam19h.bin +index 02a5d05..50470c3 100644 +Binary files a/amd-ucode/microcode_amd_fam19h.bin and b/amd-ucode/microcode_amd_fam19h.bin differ +diff --git a/amd-ucode/microcode_amd_fam19h.bin.asc b/amd-ucode/microcode_amd_fam19h.bin.asc +index 8cff901..a32b4d6 100644 +--- a/amd-ucode/microcode_amd_fam19h.bin.asc ++++ b/amd-ucode/microcode_amd_fam19h.bin.asc +@@ -1,11 +1,11 @@ + -----BEGIN PGP SIGNATURE----- + +-iQEzBAABCgAdFiEE/HxsUF2vzBRxg1fK5L5TOfMornMFAmTEYrcACgkQ5L5TOfMo +-rnN4IQf/QKbOezXZ4OYzaPANvsZQEAzLNfuylC/aQMwrPaO7daz5/zmCN4HU5XkH +-dDT8DYfPg+fQHIgxAw0/L24xPOm5Op/QuLVDyDqVr4qvL8+65eeI+JqxD/wXMXYN +-V34kkLM2p8iuyY1Nc8IDLXu4X75KGNPbKZlMRKMU3Pr7ai5O4ihmiAM+N6qv1KEJ +-YToNN6vrg0qt1cv0SLM8sa4e7L1+oblUrg/o0FViYE8pxsU3ZRRVSJMUg+lKjvl/ +-1ZPGKOdD80fcNJ+ItYGHNNs3eCc3WgW7Kc/E668eH75Yu9Zt7ewWZX8Sg/mygleY +-OzMwhbPJg4bF4zm7C/Pku7i1T2Omcg== +-=km2X ++iQEzBAABCgAdFiEE/HxsUF2vzBRxg1fK5L5TOfMornMFAmS3F00ACgkQ5L5TOfMo ++rnNEhQgAizSV8IFpvaYNytaJKLA4uevrZneGPV4czjCXnnj1yHpfQmCTyZQnoLnx ++7gyzf7K5271zO51FBQ5z2Nm48a3XPUhMbQLNP4BZdekLiA3bRpMtSyHct6zD0ULm ++xaFaOQ7MR1tGADhlon1bDvtnOuixUhwrZhEIlR9MzQAzERKDMOAVTbxn9ZhMfYiT ++LhA791Blyyi+6Z9uh7BpaA8l8uvoxt+uuvlBTjQMR3ER/TEjgcsoy+XhhK4QKS0V ++wJCtcDle/3pF+N6SAFWiXbNZ+P8p19afhcYddDl97xtpzA6/8b20a2eHkrqnu/Ds ++jTozF9kmhiifYMYpXtXgSOwI3GRZbQ== ++=t+j1 + -----END PGP SIGNATURE----- diff --git a/system/hosts/nixmsi.nix b/system/hosts/nixmsi.nix index 8cea0d3..3331998 100644 --- a/system/hosts/nixmsi.nix +++ b/system/hosts/nixmsi.nix @@ -120,6 +120,7 @@ # programs.wireshark.enable = true; # users.groups.wireshark.members = [ config.common.mainUsername ]; services.printing.enable = true; + services.printing.drivers = [ pkgs.hplip ]; services.pipewire = { enable = true; alsa.enable = true; @@ -171,6 +172,8 @@ services.udev.packages = [ pkgs.android-udev-rules ]; + services.avahi.enable = true; + services.avahi.nssmdns = true; environment.systemPackages = with pkgs; [ comma neovim diff --git a/system/hosts/nixserver/home.nix b/system/hosts/nixserver/home.nix index 745b590..b430e6c 100644 --- a/system/hosts/nixserver/home.nix +++ b/system/hosts/nixserver/home.nix @@ -47,6 +47,7 @@ in { ''; # locations."/.well-known/acme-challenge".extraConfig = "auth_basic off;"; locations."/".root = "/var/www/home.${cfg.domainName}/"; + locations."/scan/".proxyPass = "http://${lib.quoteListenAddr config.services.scanservjs.settings.host}:${toString config.services.scanservjs.settings.port}/"; locations."/grafana/" = { proxyPass = "http://grafana/"; proxyWebsockets = true; @@ -383,4 +384,12 @@ in { publish.addresses = true; publish.userServices = true; }; + hardware.sane = { + enable = true; + extraBackends = with pkgs; [ hplipWithPlugin ]; + }; + nixpkgs.config.allowUnfreePredicate = pkg: lib.getName pkg == "hplip"; + services.scanservjs.enable = true; + services.scanservjs.settings.host = "127.0.0.1"; + services.scanservjs.settings.port = 3952; } diff --git a/system/modules/scanservjs.nix b/system/modules/scanservjs.nix new file mode 100644 index 0000000..29a67c6 --- /dev/null +++ b/system/modules/scanservjs.nix @@ -0,0 +1,151 @@ +{ config +, lib +, pkgs +, ...}: + +let + cfg = config.services.scanservjs; + /* + substituteInPlace src/classes/config.js \ + --replace '/usr/bin/scanimage' '${sane-backends}/bin/scanimage' \ + --replace '/usr/bin/convert' '${imagemagick}/bin/convert' \ + --replace '/usr/bin/tesseract' '${tesseract}/bin/tesseract' + */ + settings = { + scanimage = "${pkgs.sane-backends}/bin/scanimage"; + convert = "${pkgs.imagemagick}/bin/convert"; + tesseract = "${pkgs.tesseract}/bin/tesseract"; + # it defaults to config/devices.json, but "config" dir doesn't exist 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; + + # config.host = '127.0.0.1'; + # config.port = 8080; + # config.devices = []; + # config.ocrLanguage = 'eng'; + # config.log.level = 'DEBUG'; + # config.scanimage = '/usr/bin/scanimage'; + # config.convert = '/usr/bin/convert'; + # config.tesseract = '/usr/bin/tesseract'; + # config.previewResolution = 100; + 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 (lib.mdDoc "scanservjs"); + stateDir = lib.mkOption { + type = lib.types.str; + default = "/var/lib/scanservjs"; + description = lib.mdDoc '' + State directory for scanservjs + ''; + }; + settings = lib.mkOption { + default = {}; + description = lib.mdDoc '' + 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 = lib.mdDoc '' + Extra code to add to config.local.js's `afterConfig` + ''; + }; + extraDevicesConfig = lib.mkOption { + default = ""; + type = lib.types.lines; + description = lib.mdDoc '' + Extra code to add to config.local.js's `afterDevices` + ''; + }; + runAfterScan = lib.mkOption { + default = ""; + type = lib.types.lines; + description = lib.mdDoc '' + 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... + path = with pkgs; [ coreutils sane-backends imagemagick tesseract ]; + environment.NIX_SCANSERVJS_CONFIG_PATH = configFile; + environment.SANE_CONFIG_DIR = "/etc/sane-config"; + environment.LD_LIBRARY_PATH = "/etc/sane-libs"; + serviceConfig = { + ExecStart = "${package}/bin/scanservjs"; + Restart = "always"; + User = "scanservjs"; + Group = "scanservjs"; + WorkingDirectory = cfg.stateDir; + }; + }; + }; +}