diff --git a/maintainers/maintainer-list.nix b/maintainers/maintainer-list.nix index 004898a98ced..f3e2d4bafb09 100644 --- a/maintainers/maintainer-list.nix +++ b/maintainers/maintainer-list.nix @@ -1425,6 +1425,12 @@ githubId = 4194320; name = "Anton Schirg"; }; + aorith = { + email = "aomanu+nixpkgs@gmail.com"; + github = "aorith"; + githubId = 5411704; + name = "Manuel Sanchez Pinar"; + }; apeschar = { email = "albert@peschar.net"; github = "apeschar"; diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index e07295b4ba51..d4e0e689e28c 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1389,6 +1389,7 @@ ./services/web-apps/rss-bridge.nix ./services/web-apps/selfoss.nix ./services/web-apps/shiori.nix + ./services/web-apps/silverbullet.nix ./services/web-apps/slskd.nix ./services/web-apps/snipe-it.nix ./services/web-apps/sogo.nix diff --git a/nixos/modules/services/web-apps/silverbullet.nix b/nixos/modules/services/web-apps/silverbullet.nix new file mode 100644 index 000000000000..a0c6ee34d262 --- /dev/null +++ b/nixos/modules/services/web-apps/silverbullet.nix @@ -0,0 +1,123 @@ +{ config +, pkgs +, lib +, ... +}: +let + cfg = config.services.silverbullet; + defaultUser = "silverbullet"; + defaultGroup = defaultUser; + defaultSpaceDir = "/var/lib/silverbullet"; +in +{ + options = { + services.silverbullet = { + enable = lib.mkEnableOption (lib.mdDoc "Silverbullet, an open-source, self-hosted, offline-capable Personal Knowledge Management (PKM) web application."); + + package = lib.mkPackageOptionMD pkgs "silverbullet" { }; + + openFirewall = lib.mkOption { + type = lib.types.bool; + default = false; + description = lib.mdDoc "Open port in the firewall."; + }; + + listenPort = lib.mkOption { + type = lib.types.int; + default = 3000; + description = lib.mdDoc "Port to listen on."; + }; + + listenAddress = lib.mkOption { + type = lib.types.str; + default = "127.0.0.1"; + description = lib.mdDoc "Address or hostname to listen on. Defaults to 127.0.0.1."; + }; + + spaceDir = lib.mkOption { + type = lib.types.path; + default = defaultSpaceDir; + example = "/home/yourUser/silverbullet"; + description = lib.mdDoc '' + Folder to store Silverbullet's space/workspace. + By default it is located at `${defaultSpaceDir}`. + ''; + }; + + user = lib.mkOption { + type = lib.types.str; + default = defaultUser; + example = "yourUser"; + description = lib.mdDoc '' + The user to run Silverbullet as. + By default, a user named `${defaultUser}` will be created whose space + directory is [spaceDir](#opt-services.silverbullet.spaceDir). + ''; + }; + + group = lib.mkOption { + type = lib.types.str; + default = defaultGroup; + example = "yourGroup"; + description = lib.mdDoc '' + The group to run Silverbullet under. + By default, a group named `${defaultGroup}` will be created. + ''; + }; + + envFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + example = "/etc/silverbullet.env"; + description = lib.mdDoc '' + File containing extra environment variables. For example: + + ``` + SB_USER=user:password + SB_AUTH_TOKEN=abcdefg12345 + ``` + ''; + }; + + extraArgs = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + example = [ "--db /path/to/silverbullet.db" ]; + description = lib.mdDoc "Extra arguments passed to silverbullet."; + }; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.silverbullet = { + description = "Silverbullet service"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + preStart = lib.mkIf (!lib.hasPrefix "/var/lib/" cfg.spaceDir) "mkdir -p '${cfg.spaceDir}'"; + serviceConfig = { + Type = "simple"; + User = "${cfg.user}"; + Group = "${cfg.group}"; + EnvironmentFile = lib.mkIf (cfg.envFile != null) "${cfg.envFile}"; + StateDirectory = lib.mkIf (lib.hasPrefix "/var/lib/" cfg.spaceDir) (lib.last (lib.splitString "/" cfg.spaceDir)); + ExecStart = "${lib.getExe cfg.package} --port ${toString cfg.listenPort} --hostname '${cfg.listenAddress}' '${cfg.spaceDir}' " + lib.concatStringsSep " " cfg.extraArgs; + Restart = "on-failure"; + }; + }; + + networking.firewall = lib.mkIf cfg.openFirewall { + allowedTCPPorts = [ cfg.listenPort ]; + }; + + users.users.${defaultUser} = lib.mkIf (cfg.user == defaultUser) { + isSystemUser = true; + group = cfg.group; + description = "Silverbullet daemon user"; + }; + + users.groups.${defaultGroup} = lib.mkIf (cfg.group == defaultGroup) { }; + }; + + meta.maintainers = with lib.maintainers; [ aorith ]; +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 487a7755bb17..bc586a609271 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -811,6 +811,7 @@ in { shattered-pixel-dungeon = handleTest ./shattered-pixel-dungeon.nix {}; shiori = handleTest ./shiori.nix {}; signal-desktop = handleTest ./signal-desktop.nix {}; + silverbullet = handleTest ./silverbullet.nix {}; simple = handleTest ./simple.nix {}; sing-box = handleTest ./sing-box.nix {}; slimserver = handleTest ./slimserver.nix {}; diff --git a/nixos/tests/silverbullet.nix b/nixos/tests/silverbullet.nix new file mode 100644 index 000000000000..e7e3cf536558 --- /dev/null +++ b/nixos/tests/silverbullet.nix @@ -0,0 +1,47 @@ +import ./make-test-python.nix ({ lib, ... }: { + name = "silverbullet"; + meta.maintainers = with lib.maintainers; [ aorith ]; + + nodes.simple = { ... }: { + services.silverbullet.enable = true; + }; + + nodes.configured = { pkgs, ... }: { + users.users.test.isNormalUser = true; + users.groups.test = { }; + + services.silverbullet = { + enable = true; + package = pkgs.silverbullet; + listenPort = 3001; + listenAddress = "localhost"; + spaceDir = "/home/test/silverbullet"; + user = "test"; + group = "test"; + envFile = pkgs.writeText "silverbullet.env" '' + SB_USER=user:password + SB_AUTH_TOKEN=test + ''; + extraArgs = [ "--reindex" "--db /home/test/silverbullet/custom.db" ]; + }; + }; + + testScript = { nodes, ... }: '' + PORT = ${builtins.toString nodes.simple.services.silverbullet.listenPort} + ADDRESS = "${nodes.simple.services.silverbullet.listenAddress}" + SPACEDIR = "${nodes.simple.services.silverbullet.spaceDir}" + simple.wait_for_unit("silverbullet.service") + simple.wait_for_open_port(PORT) + simple.succeed(f"curl --max-time 5 -s -v -o /dev/null --fail http://{ADDRESS}:{PORT}/") + simple.succeed(f"test -d '{SPACEDIR}'") + + PORT = ${builtins.toString nodes.configured.services.silverbullet.listenPort} + ADDRESS = "${nodes.configured.services.silverbullet.listenAddress}" + SPACEDIR = "${nodes.configured.services.silverbullet.spaceDir}" + configured.wait_for_unit("silverbullet.service") + configured.wait_for_open_port(PORT) + assert int(configured.succeed(f"curl --max-time 5 -s -o /dev/null -w '%{{http_code}}' -XPUT -d 'test' --fail http://{ADDRESS}:{PORT}/test.md -H'Authorization: Bearer test'")) == 200 + assert int(configured.fail(f"curl --max-time 5 -s -o /dev/null -w '%{{http_code}}' -XPUT -d 'test' --fail http://{ADDRESS}:{PORT}/test.md -H'Authorization: Bearer wrong'")) == 401 + configured.succeed(f"test -d '{SPACEDIR}'") + ''; +}) diff --git a/pkgs/by-name/si/silverbullet/package.nix b/pkgs/by-name/si/silverbullet/package.nix new file mode 100644 index 000000000000..b8732b4c6997 --- /dev/null +++ b/pkgs/by-name/si/silverbullet/package.nix @@ -0,0 +1,39 @@ +{ lib +, stdenv +, fetchurl +, deno +, makeWrapper +}: +stdenv.mkDerivation (finalAttrs: { + pname = "silverbullet"; + version = "0.7.6"; + + src = fetchurl { + url = "https://github.com/silverbulletmd/silverbullet/releases/download/${finalAttrs.version}/silverbullet.js"; + hash = "sha256-bnD9iZVRIg6otgDi4yNWySqmJsJJIVnjFGsTrGCMw/o="; + }; + + dontUnpack = true; + + nativeBuildInputs = [ makeWrapper ]; + + installPhase = '' + runHook preInstall + mkdir -p $out/{bin,lib} + cp $src $out/lib/silverbullet.js + makeWrapper ${lib.getExe deno} $out/bin/silverbullet \ + --set DENO_NO_UPDATE_CHECK "1" \ + --add-flags "run -A --unstable-kv --unstable-worker-options ${placeholder "out"}/lib/silverbullet.js" + runHook postInstall + ''; + + meta = { + changelog = "https://github.com/silverbulletmd/silverbullet/blob/${finalAttrs.version}/website/CHANGELOG.md"; + description = "An open-source, self-hosted, offline-capable Personal Knowledge Management (PKM) web application"; + homepage = "https://silverbullet.md"; + license = lib.licenses.mit; + maintainers = with lib.maintainers; [ aorith ]; + mainProgram = "silverbullet"; + inherit (deno.meta) platforms; + }; +})