nixos/cross-seed: init module
This commit is contained in:
parent
e825e5bc2a
commit
3b3bced9c5
@ -111,6 +111,8 @@
|
||||
|
||||
- [autobrr](https://autobrr.com), a modern download automation tool for torrents and usenets. Available as [services.autobrr](#opt-services.autobrr.enable).
|
||||
|
||||
- [cross-seed](https://www.cross-seed.org), a tool to set-up fully automatic cross-seeding of torrents. Available as [services.cross-seed](#opt-services.cross-seed.enable).
|
||||
|
||||
- [agorakit](https://github.com/agorakit/agorakit), an organization tool for citizens' collectives. Available with [services.agorakit](options.html#opt-services.agorakit.enable).
|
||||
|
||||
- [vivid](https://github.com/sharkdp/vivid), a generator for LS_COLOR. Available as [programs.vivid](#opt-programs.vivid.enable).
|
||||
|
||||
@ -1424,6 +1424,7 @@
|
||||
./services/system/userborn.nix
|
||||
./services/system/zram-generator.nix
|
||||
./services/torrent/bitmagnet.nix
|
||||
./services/torrent/cross-seed.nix
|
||||
./services/torrent/deluge.nix
|
||||
./services/torrent/flexget.nix
|
||||
./services/torrent/flood.nix
|
||||
|
||||
214
nixos/modules/services/torrent/cross-seed.nix
Normal file
214
nixos/modules/services/torrent/cross-seed.nix
Normal file
@ -0,0 +1,214 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.services.cross-seed;
|
||||
|
||||
inherit (lib)
|
||||
mkEnableOption
|
||||
mkPackageOption
|
||||
mkOption
|
||||
types
|
||||
;
|
||||
settingsFormat = pkgs.formats.json { };
|
||||
in
|
||||
{
|
||||
options.services.cross-seed = {
|
||||
enable = mkEnableOption "cross-seed";
|
||||
|
||||
package = mkPackageOption pkgs "cross-seed" { };
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = "cross-seed";
|
||||
description = "User to run cross-seed as.";
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
type = types.str;
|
||||
default = "cross-seed";
|
||||
example = "torrents";
|
||||
description = "Group to run cross-seed as.";
|
||||
};
|
||||
|
||||
configDir = mkOption {
|
||||
type = types.path;
|
||||
default = "/var/lib/cross-seed";
|
||||
description = "Cross-seed config directory";
|
||||
};
|
||||
|
||||
settings = mkOption {
|
||||
default = { };
|
||||
type = types.submodule {
|
||||
freeformType = settingsFormat.type;
|
||||
options = {
|
||||
dataDirs = mkOption {
|
||||
type = types.listOf types.path;
|
||||
default = [ ];
|
||||
description = ''
|
||||
Paths to be searched for matching data.
|
||||
|
||||
If you use Injection, cross-seed will use the specified linkType
|
||||
to create a link to the original file in the linkDirs.
|
||||
|
||||
If linkType is hardlink, these must be on the same volume as the
|
||||
data.
|
||||
'';
|
||||
};
|
||||
|
||||
linkDirs = mkOption {
|
||||
type = types.listOf types.path;
|
||||
default = [ ];
|
||||
description = ''
|
||||
List of directories where cross-seed will create links.
|
||||
|
||||
If linkType is hardlink, these must be on the same volume as the data.
|
||||
'';
|
||||
};
|
||||
|
||||
torrentDir = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = ''
|
||||
Directory containing torrent files, or if you're using a torrent
|
||||
client integration and injection - your torrent client's .torrent
|
||||
file store/cache.
|
||||
'';
|
||||
};
|
||||
|
||||
outputDir = mkOption {
|
||||
type = types.path;
|
||||
default = "${cfg.configDir}/output";
|
||||
defaultText = ''''${cfg.configDir}/output'';
|
||||
description = "Directory where cross-seed will place torrent files it finds.";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 2468;
|
||||
example = 3000;
|
||||
description = "Port the cross-seed daemon listens on.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
description = ''
|
||||
Configuration options for cross-seed.
|
||||
|
||||
Secrets should not be set in this option, as they will be available in
|
||||
the Nix store. For secrets, please use settingsFile.
|
||||
|
||||
For more details, see [the cross-seed documentation](https://www.cross-seed.org/docs/basics/options).
|
||||
'';
|
||||
};
|
||||
|
||||
settingsFile = lib.mkOption {
|
||||
default = null;
|
||||
type = types.nullOr types.path;
|
||||
description = ''
|
||||
Path to a JSON file containing settings that will be merged with the
|
||||
settings option. This is suitable for storing secrets, as they will not
|
||||
be exposed on the Nix store.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config =
|
||||
let
|
||||
jsonSettingsFile = settingsFormat.generate "settings.json" cfg.settings;
|
||||
|
||||
# Since cross-seed uses a javascript config file, we can use node's
|
||||
# ability to parse JSON directly to avoid having to do any conversion.
|
||||
# This also means we don't need to use any external programs to merge the
|
||||
# secrets.
|
||||
secretSettingsSegment =
|
||||
lib.optionalString (cfg.settingsFile != null) # js
|
||||
''
|
||||
const path = require("node:path");
|
||||
const secret_settings_json = path.join(process.env.CREDENTIALS_DIRECTORY, "secretSettingsFile");
|
||||
Object.assign(loaded_settings, JSON.parse(fs.readFileSync(secret_settings_json, "utf8")));
|
||||
'';
|
||||
|
||||
javascriptConfig =
|
||||
pkgs.writeText "config.js" # js
|
||||
''
|
||||
"use strict";
|
||||
const fs = require("fs");
|
||||
const settings_json = "${jsonSettingsFile}";
|
||||
let loaded_settings = JSON.parse(fs.readFileSync(settings_json, "utf8"));
|
||||
${secretSettingsSegment}
|
||||
module.exports = loaded_settings;
|
||||
'';
|
||||
in
|
||||
lib.mkIf (cfg.enable) {
|
||||
assertions = [
|
||||
{
|
||||
assertion = !(cfg.settings ? apiKey);
|
||||
message = ''
|
||||
The API key should be set via the settingsFile option, to avoid
|
||||
exposing it on the Nix store.
|
||||
'';
|
||||
}
|
||||
];
|
||||
|
||||
systemd.tmpfiles.settings."10-cross-seed"."${cfg.configDir}".d = {
|
||||
inherit (cfg) group user;
|
||||
mode = "700";
|
||||
};
|
||||
|
||||
systemd.services.cross-seed = {
|
||||
description = "cross-seed";
|
||||
after = [ "network-online.target" ];
|
||||
wants = [ "network-online.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
environment.CONFIG_DIR = cfg.configDir;
|
||||
preStart = ''
|
||||
install -D -m 600 -o '${cfg.user}' -g '${cfg.group}' '${javascriptConfig}' '${cfg.configDir}/config.js'
|
||||
'';
|
||||
|
||||
serviceConfig = {
|
||||
ExecStart = "${lib.getExe cfg.package} daemon";
|
||||
User = cfg.user;
|
||||
Group = cfg.group;
|
||||
|
||||
# Only allow binding to the specified port.
|
||||
SocketBindDeny = "any";
|
||||
SocketBindAllow = cfg.settings.port;
|
||||
|
||||
LoadCredential = lib.mkIf (cfg.settingsFile != null) "secretSettingsFile:${cfg.settingsFile}";
|
||||
|
||||
StateDirectory = "cross-seed";
|
||||
ReadWritePaths = [ cfg.settings.outputDir ];
|
||||
ReadOnlyPaths = lib.optional (cfg.settings.torrentDir != null) cfg.settings.torrentDir;
|
||||
};
|
||||
|
||||
unitConfig = {
|
||||
# Unfortunately, we can not protect these if we are to hardlink between them, as they need to be on the same volume for hardlinks to work.
|
||||
RequiresMountsFor = lib.flatten [
|
||||
cfg.settings.dataDirs
|
||||
cfg.settings.linkDirs
|
||||
cfg.settings.outputDir
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
# It's useful to have the package in the path, to be able to e.g. get the API key.
|
||||
environment.systemPackages = [ cfg.package ];
|
||||
|
||||
users.users = lib.mkIf (cfg.user == "cross-seed") {
|
||||
cross-seed = {
|
||||
group = cfg.group;
|
||||
description = "cross-seed user";
|
||||
isSystemUser = true;
|
||||
home = cfg.configDir;
|
||||
};
|
||||
};
|
||||
|
||||
users.groups = lib.mkIf (cfg.group == "cross-seed") {
|
||||
cross-seed = { };
|
||||
};
|
||||
};
|
||||
}
|
||||
@ -253,6 +253,7 @@ in {
|
||||
curl-impersonate = handleTest ./curl-impersonate.nix {};
|
||||
custom-ca = handleTest ./custom-ca.nix {};
|
||||
croc = handleTest ./croc.nix {};
|
||||
cross-seed = runTest ./cross-seed.nix;
|
||||
cyrus-imap = runTest ./cyrus-imap.nix;
|
||||
darling = handleTest ./darling.nix {};
|
||||
darling-dmg = runTest ./darling-dmg.nix;
|
||||
|
||||
43
nixos/tests/cross-seed.nix
Normal file
43
nixos/tests/cross-seed.nix
Normal file
@ -0,0 +1,43 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
apiKey = "twentyfourcharacterskey!";
|
||||
in
|
||||
{
|
||||
name = "cross-seed";
|
||||
meta.maintainers = with lib.maintainers; [ pta2002 ];
|
||||
|
||||
nodes.machine =
|
||||
{ pkgs, config, ... }:
|
||||
let
|
||||
cfg = config.services.cross-seed;
|
||||
in
|
||||
{
|
||||
systemd.tmpfiles.settings."0-cross-seed-test"."${cfg.settings.torrentDir}".d = {
|
||||
inherit (cfg) user group;
|
||||
mode = "700";
|
||||
};
|
||||
|
||||
services.cross-seed = {
|
||||
enable = true;
|
||||
settings = {
|
||||
outputDir = "/var/lib/cross-seed/output";
|
||||
torrentDir = "/var/lib/torrents";
|
||||
torznab = [ ];
|
||||
useClientTorrents = false;
|
||||
};
|
||||
# # We create this secret in the Nix store (making it readable by everyone).
|
||||
# # DO NOT DO THIS OUTSIDE OF TESTS!!
|
||||
settingsFile = (pkgs.formats.json { }).generate "secrets.json" {
|
||||
inherit apiKey;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript = # python
|
||||
''
|
||||
start_all()
|
||||
machine.wait_for_unit("cross-seed.service")
|
||||
machine.wait_for_open_port(2468)
|
||||
machine.succeed("curl --fail -XPOST http://localhost:2468/api/search?apiKey=${apiKey}")
|
||||
'';
|
||||
}
|
||||
@ -2,6 +2,7 @@
|
||||
lib,
|
||||
buildNpmPackage,
|
||||
fetchFromGitHub,
|
||||
nixosTests,
|
||||
}:
|
||||
|
||||
buildNpmPackage rec {
|
||||
@ -17,6 +18,8 @@ buildNpmPackage rec {
|
||||
|
||||
npmDepsHash = "sha256-hqQi0kSPm9SKEoLu6InvRMPxbQ+CBpKVPJhhOdo2ZII=";
|
||||
|
||||
passthru.tests.cross-seed = nixosTests.cross-seed;
|
||||
|
||||
meta = {
|
||||
description = "Fully-automatic torrent cross-seeding with Torznab";
|
||||
homepage = "https://cross-seed.org";
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user