mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-06-12 20:55:31 +03:00
nixos/cross-seed: init module
This commit is contained in:
parent
e825e5bc2a
commit
3b3bced9c5
6 changed files with 264 additions and 0 deletions
|
@ -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).
|
- [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).
|
- [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).
|
- [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/userborn.nix
|
||||||
./services/system/zram-generator.nix
|
./services/system/zram-generator.nix
|
||||||
./services/torrent/bitmagnet.nix
|
./services/torrent/bitmagnet.nix
|
||||||
|
./services/torrent/cross-seed.nix
|
||||||
./services/torrent/deluge.nix
|
./services/torrent/deluge.nix
|
||||||
./services/torrent/flexget.nix
|
./services/torrent/flexget.nix
|
||||||
./services/torrent/flood.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 {};
|
curl-impersonate = handleTest ./curl-impersonate.nix {};
|
||||||
custom-ca = handleTest ./custom-ca.nix {};
|
custom-ca = handleTest ./custom-ca.nix {};
|
||||||
croc = handleTest ./croc.nix {};
|
croc = handleTest ./croc.nix {};
|
||||||
|
cross-seed = runTest ./cross-seed.nix;
|
||||||
cyrus-imap = runTest ./cyrus-imap.nix;
|
cyrus-imap = runTest ./cyrus-imap.nix;
|
||||||
darling = handleTest ./darling.nix {};
|
darling = handleTest ./darling.nix {};
|
||||||
darling-dmg = runTest ./darling-dmg.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,
|
lib,
|
||||||
buildNpmPackage,
|
buildNpmPackage,
|
||||||
fetchFromGitHub,
|
fetchFromGitHub,
|
||||||
|
nixosTests,
|
||||||
}:
|
}:
|
||||||
|
|
||||||
buildNpmPackage rec {
|
buildNpmPackage rec {
|
||||||
|
@ -17,6 +18,8 @@ buildNpmPackage rec {
|
||||||
|
|
||||||
npmDepsHash = "sha256-hqQi0kSPm9SKEoLu6InvRMPxbQ+CBpKVPJhhOdo2ZII=";
|
npmDepsHash = "sha256-hqQi0kSPm9SKEoLu6InvRMPxbQ+CBpKVPJhhOdo2ZII=";
|
||||||
|
|
||||||
|
passthru.tests.cross-seed = nixosTests.cross-seed;
|
||||||
|
|
||||||
meta = {
|
meta = {
|
||||||
description = "Fully-automatic torrent cross-seeding with Torznab";
|
description = "Fully-automatic torrent cross-seeding with Torznab";
|
||||||
homepage = "https://cross-seed.org";
|
homepage = "https://cross-seed.org";
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue