Unverified Commit 1d48df8a authored by dotlambda's avatar dotlambda Committed by GitHub
Browse files

shelfmark: init at 1.2.0, add NixOS module and test (#498942)

parents 8cbb7499 e1df3ce7
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -957,6 +957,7 @@
  ./services/misc/servarr/sonarr.nix
  ./services/misc/servarr/whisparr.nix
  ./services/misc/serviio.nix
  ./services/misc/shelfmark.nix
  ./services/misc/shoko.nix
  ./services/misc/sickbeard.nix
  ./services/misc/snapper.nix
+131 −0
Original line number Diff line number Diff line
{
  lib,
  pkgs,
  config,
  ...
}:
let
  cfg = config.services.shelfmark;
in
{
  options.services.shelfmark = {

    enable = lib.mkEnableOption "Shelfmark, a self-hosted book and audiobook search and download interface";

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

    openFirewall = lib.mkOption {
      type = lib.types.bool;
      default = false;
      description = "Open the appropriate ports in the firewall for Shelfmark.";
    };

    environment = lib.mkOption {
      type = lib.types.submodule {
        freeformType = lib.types.attrsOf lib.types.str;
        options = {
          FLASK_HOST = lib.mkOption {
            type = lib.types.str;
            default = "127.0.0.1";
            example = "0.0.0.0";
            description = "The IP address to bind the Shelfmark server to.";
          };

          FLASK_PORT = lib.mkOption {
            type = lib.types.port;
            default = 8084;
            apply = toString;
            description = "TCP port for the Shelfmark web interface.";
          };

          ENABLE_LOGGING = lib.mkOption {
            type = lib.types.bool;
            apply = toString;
            default = false;
            description = "Whether to enable file logging. Disabled by default since systemd captures console output via journald.";
          };

          CONFIG_DIR = lib.mkOption {
            type = lib.types.path;
            default = "/var/lib/shelfmark";
            description = "Directory for Shelfmark configuration, database, and artwork cache.";
          };
        };
      };
      default = { };
      example = {
        SEARCH_MODE = "universal";
        LOG_LEVEL = "DEBUG";
      };
      description = ''
        Environment variables to pass to the Shelfmark service.
        See <https://github.com/calibrain/shelfmark/blob/main/docs/environment-variables.md>
        for available options.
      '';
    };
  };

  config = lib.mkIf cfg.enable {

    systemd.services.shelfmark = {
      description = "Shelfmark - book and audiobook search and download interface";
      after = [ "network-online.target" ];
      wants = [ "network-online.target" ];
      wantedBy = [ "multi-user.target" ];
      inherit (cfg) environment;
      serviceConfig = {
        DynamicUser = true;
        ExecStart = "${lib.getExe cfg.package} -b ${cfg.environment.FLASK_HOST}:${cfg.environment.FLASK_PORT}";
        StateDirectory = "shelfmark";

        ProtectSystem = "strict";
        ProtectHome = true;
        PrivateTmp = true;
        PrivateDevices = true;
        PrivateMounts = true;
        ProtectControlGroups = true;
        ProtectKernelTunables = true;
        RestrictSUIDSGID = true;
        RemoveIPC = true;
        UMask = "0077";

        CapabilityBoundingSet = [ "" ];
        NoNewPrivileges = true;

        ProtectKernelModules = true;
        ProtectKernelLogs = true;
        ProtectClock = true;
        SystemCallArchitectures = "native";
        SystemCallFilter = [
          "@system-service"
          "~@privileged"
          "~@resources"
        ];

        RestrictAddressFamilies = [
          "AF_INET"
          "AF_INET6"
          "AF_UNIX"
        ];

        PrivateUsers = true;

        LockPersonality = true;
        ProtectHostname = true;
        RestrictRealtime = true;
        RestrictNamespaces = true;
        ProtectProc = "invisible";
        ProcSubset = "pid";
        DeviceAllow = [ "" ];
      };
    };

    networking.firewall = lib.mkIf cfg.openFirewall {
      allowedTCPPorts = [ (lib.toInt cfg.environment.FLASK_PORT) ];
    };
  };

  meta = {
    maintainers = with lib.maintainers; [ jamiemagee ];
  };
}
+1 −0
Original line number Diff line number Diff line
@@ -1461,6 +1461,7 @@ in
  shadps4 = runTest ./shadps4.nix;
  sharkey = runTest ./web-apps/sharkey.nix;
  shattered-pixel-dungeon = runTest ./shattered-pixel-dungeon.nix;
  shelfmark = runTest ./shelfmark.nix;
  shiori = runTest ./shiori.nix;
  shoko = import ./shoko.nix { inherit runTest; };
  signal-desktop = runTest ./signal-desktop.nix;
+20 −0
Original line number Diff line number Diff line
{ lib, ... }:
{
  name = "shelfmark";
  meta.maintainers = with lib.maintainers; [ jamiemagee ];

  nodes.machine = {
    services.shelfmark = {
      enable = true;
      environment.FLASK_HOST = "0.0.0.0";
    };
  };

  testScript = ''
    machine.wait_for_unit("shelfmark.service")
    machine.wait_for_open_port(8084)

    with subtest("Health endpoint responds"):
        machine.succeed("curl --fail http://localhost:8084/api/health")
  '';
}
+25 −0
Original line number Diff line number Diff line
{
  buildNpmPackage,
  shelfmark,
}:

buildNpmPackage (finalAttrs: {
  pname = "shelfmark-frontend";
  inherit (shelfmark) version src;

  sourceRoot = "${finalAttrs.src.name}/src/frontend";

  npmDepsHash = "sha256-RAzotFGj0FGpfF7iyB5f2fdKFvMLcpJx142yplRwboU=";

  installPhase = ''
    runHook preInstall
    cp -r dist $out
    runHook postInstall
  '';

  meta = {
    description = "Shelfmark frontend";
    homepage = "https://github.com/calibrain/shelfmark/tree/main/src/frontend";
    inherit (shelfmark.meta) changelog license maintainers;
  };
})
Loading