All checks were successful
Build and Deploy / deploy (push) Successful in 1m12s
114 lines
3.8 KiB
Nix
114 lines
3.8 KiB
Nix
{
|
|
pkgs,
|
|
service_configs,
|
|
config,
|
|
lib,
|
|
...
|
|
}:
|
|
let
|
|
prowlarrPort = toString service_configs.ports.private.prowlarr.port;
|
|
sonarrPort = toString service_configs.ports.private.sonarr.port;
|
|
radarrPort = toString service_configs.ports.private.radarr.port;
|
|
bitmagnetPort = toString service_configs.ports.private.bitmagnet.port;
|
|
bridgeAddr = config.vpnNamespaces.wg.bridgeAddress;
|
|
|
|
prowlarrConfigXml = "${service_configs.prowlarr.dataDir}/config.xml";
|
|
sonarrConfigXml = "${service_configs.sonarr.dataDir}/config.xml";
|
|
radarrConfigXml = "${service_configs.radarr.dataDir}/config.xml";
|
|
|
|
curl = "${pkgs.curl}/bin/curl";
|
|
jq = "${pkgs.jq}/bin/jq";
|
|
|
|
# Clears the escalating failure backoff for the Bitmagnet indexer across
|
|
# Prowlarr, Sonarr, and Radarr so searches resume immediately after
|
|
# Bitmagnet restarts instead of waiting hours for disable timers to expire.
|
|
recoveryScript = pkgs.writeShellScript "prowlarr-bitmagnet-recovery" ''
|
|
set -euo pipefail
|
|
|
|
wait_for() {
|
|
for _ in $(seq 1 "$2"); do
|
|
${curl} -sf --max-time 5 "$1" > /dev/null && return 0
|
|
sleep 5
|
|
done
|
|
echo "$1 not reachable, aborting" >&2; exit 1
|
|
}
|
|
|
|
# Test a Bitmagnet-named indexer to clear its failure status.
|
|
# A successful test triggers RecordSuccess() which resets the backoff.
|
|
clear_status() {
|
|
local key indexer
|
|
key=$(${lib.extractArrApiKey ''"$3"''}) || return 0
|
|
indexer=$(${curl} -sf --max-time 10 \
|
|
-H "X-Api-Key: $key" "$2/api/$1/indexer" | \
|
|
${jq} 'first(.[] | select(.name | test("Bitmagnet"; "i")))') || return 0
|
|
[ -n "$indexer" ] && [ "$indexer" != "null" ] || return 0
|
|
${curl} -sf --max-time 30 \
|
|
-H "X-Api-Key: $key" -H "Content-Type: application/json" \
|
|
-X POST "$2/api/$1/indexer/test" -d "$indexer" > /dev/null
|
|
}
|
|
|
|
wait_for "http://localhost:${bitmagnetPort}" 12
|
|
wait_for "http://localhost:${prowlarrPort}/ping" 6
|
|
|
|
# Prowlarr first — downstream apps route searches through it.
|
|
clear_status v1 "http://localhost:${prowlarrPort}" "${prowlarrConfigXml}" || true
|
|
clear_status v3 "http://${bridgeAddr}:${sonarrPort}" "${sonarrConfigXml}" || true
|
|
clear_status v3 "http://${bridgeAddr}:${radarrPort}" "${radarrConfigXml}" || true
|
|
'';
|
|
in
|
|
{
|
|
imports = [
|
|
(lib.vpnNamespaceOpenPort service_configs.ports.private.bitmagnet.port "bitmagnet")
|
|
(lib.mkCaddyReverseProxy {
|
|
subdomain = "bitmagnet";
|
|
port = service_configs.ports.private.bitmagnet.port;
|
|
auth = true;
|
|
vpn = true;
|
|
})
|
|
];
|
|
|
|
services.bitmagnet = {
|
|
enable = true;
|
|
|
|
settings = {
|
|
postgres = {
|
|
host = service_configs.postgres.socket;
|
|
};
|
|
http_server = {
|
|
# TODO! make issue about this being a string and not a `port` type
|
|
port = ":" + (toString service_configs.ports.private.bitmagnet.port);
|
|
};
|
|
};
|
|
};
|
|
|
|
# The upstream default (Restart=on-failure) leaves Bitmagnet dead after
|
|
# clean exits (e.g. systemd stop during deploy). Always restart it.
|
|
systemd.services.bitmagnet.serviceConfig = {
|
|
Restart = lib.mkForce "always";
|
|
RestartSec = 10;
|
|
};
|
|
|
|
# After Bitmagnet restarts, clear the escalating failure backoff across
|
|
# Prowlarr, Sonarr, and Radarr so searches resume immediately instead of
|
|
# waiting hours for the disable timers to expire.
|
|
systemd.services.prowlarr-bitmagnet-recovery = {
|
|
description = "Clear Prowlarr/Sonarr/Radarr failure status for Bitmagnet indexer";
|
|
after = [
|
|
"bitmagnet.service"
|
|
"prowlarr.service"
|
|
"sonarr.service"
|
|
"radarr.service"
|
|
];
|
|
bindsTo = [ "bitmagnet.service" ];
|
|
wantedBy = [ "bitmagnet.service" ];
|
|
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
RemainAfterExit = true;
|
|
ExecStart = recoveryScript;
|
|
# Same VPN namespace as Bitmagnet and Prowlarr.
|
|
NetworkNamespacePath = "/run/netns/wg";
|
|
};
|
|
};
|
|
}
|