|
| 1 | +{ |
| 2 | + lib, |
| 3 | + pkgs, |
| 4 | +}: |
| 5 | +# Helper function to create a systemd service that configures Prowlarr applications via API |
| 6 | +serviceName: serviceConfig: |
| 7 | +with lib; let |
| 8 | + mkWaitForApiScript = import ../arr-common/mkWaitForApiScript.nix {inherit lib pkgs;}; |
| 9 | + capitalizedName = lib.toUpper (builtins.substring 0 1 serviceName) + builtins.substring 1 (-1) serviceName; |
| 10 | +in { |
| 11 | + description = "Configure ${serviceName} applications via API"; |
| 12 | + after = ["${serviceName}-config.service"]; |
| 13 | + requires = ["${serviceName}-config.service"]; |
| 14 | + wantedBy = ["multi-user.target"]; |
| 15 | + |
| 16 | + serviceConfig = { |
| 17 | + Type = "oneshot"; |
| 18 | + RemainAfterExit = true; |
| 19 | + ExecStartPre = mkWaitForApiScript serviceName serviceConfig; |
| 20 | + }; |
| 21 | + |
| 22 | + script = '' |
| 23 | + set -eu |
| 24 | +
|
| 25 | + # Read API key secret |
| 26 | + API_KEY=$(cat ${serviceConfig.apiKeyPath}) |
| 27 | +
|
| 28 | + BASE_URL="http://127.0.0.1:${builtins.toString serviceConfig.hostConfig.port}${serviceConfig.hostConfig.urlBase}/api/${serviceConfig.apiVersion}" |
| 29 | +
|
| 30 | + # Fetch all application schemas |
| 31 | + echo "Fetching application schemas..." |
| 32 | + SCHEMAS=$(${pkgs.curl}/bin/curl -sS -H "X-Api-Key: $API_KEY" "$BASE_URL/applications/schema") |
| 33 | +
|
| 34 | + # Fetch existing applications |
| 35 | + echo "Fetching existing applications..." |
| 36 | + APPLICATIONS=$(${pkgs.curl}/bin/curl -sS -H "X-Api-Key: $API_KEY" "$BASE_URL/applications") |
| 37 | +
|
| 38 | + # Build list of configured application names |
| 39 | + CONFIGURED_NAMES=$(cat <<'EOF' |
| 40 | + ${builtins.toJSON (map (a: a.name) serviceConfig.applications)} |
| 41 | + EOF |
| 42 | + ) |
| 43 | +
|
| 44 | + # Delete applications that are not in the configuration |
| 45 | + echo "Removing applications not in configuration..." |
| 46 | + echo "$APPLICATIONS" | ${pkgs.jq}/bin/jq -r '.[] | @json' | while IFS= read -r application; do |
| 47 | + APPLICATION_NAME=$(echo "$application" | ${pkgs.jq}/bin/jq -r '.name') |
| 48 | + APPLICATION_ID=$(echo "$application" | ${pkgs.jq}/bin/jq -r '.id') |
| 49 | +
|
| 50 | + if ! echo "$CONFIGURED_NAMES" | ${pkgs.jq}/bin/jq -e --arg name "$APPLICATION_NAME" 'index($name)' >/dev/null 2>&1; then |
| 51 | + echo "Deleting application not in config: $APPLICATION_NAME (ID: $APPLICATION_ID)" |
| 52 | + ${pkgs.curl}/bin/curl -sSf -X DELETE \ |
| 53 | + -H "X-Api-Key: $API_KEY" \ |
| 54 | + "$BASE_URL/applications/$APPLICATION_ID" >/dev/null || echo "Warning: Failed to delete application $APPLICATION_NAME" |
| 55 | + fi |
| 56 | + done |
| 57 | +
|
| 58 | + ${concatMapStringsSep "\n" (applicationConfig: let |
| 59 | + applicationName = applicationConfig.name; |
| 60 | + implementationName = applicationConfig.implementationName; |
| 61 | + inherit (applicationConfig) apiKeyPath; |
| 62 | + allOverrides = builtins.removeAttrs applicationConfig ["implementationName" "apiKeyPath"]; |
| 63 | + fieldOverrides = lib.filterAttrs (name: value: value != null && !lib.hasPrefix "_" name) allOverrides; |
| 64 | + fieldOverridesJson = builtins.toJSON fieldOverrides; |
| 65 | + in '' |
| 66 | + echo "Processing application: ${applicationName}" |
| 67 | +
|
| 68 | + apply_field_overrides() { |
| 69 | + local application_json="$1" |
| 70 | + local api_key="$2" |
| 71 | + local overrides="$3" |
| 72 | +
|
| 73 | + echo "$application_json" | ${pkgs.jq}/bin/jq \ |
| 74 | + --arg apiKey "$api_key" \ |
| 75 | + --argjson overrides "$overrides" ' |
| 76 | + .fields[] |= (if .name == "apiKey" then .value = $apiKey else . end) |
| 77 | + | . + $overrides |
| 78 | + | .fields[] |= ( |
| 79 | + . as $field | |
| 80 | + if $overrides[$field.name] != null then |
| 81 | + .value = $overrides[$field.name] |
| 82 | + else |
| 83 | + . |
| 84 | + end |
| 85 | + ) |
| 86 | + ' |
| 87 | + } |
| 88 | +
|
| 89 | + APPLICATION_API_KEY=$(cat ${apiKeyPath}) |
| 90 | + FIELD_OVERRIDES='${fieldOverridesJson}' |
| 91 | +
|
| 92 | + EXISTING_APPLICATION=$(echo "$APPLICATIONS" | ${pkgs.jq}/bin/jq -r '.[] | select(.name == "${applicationName}") | @json' || echo "") |
| 93 | +
|
| 94 | + if [ -n "$EXISTING_APPLICATION" ]; then |
| 95 | + echo "Application ${applicationName} already exists, updating..." |
| 96 | + APPLICATION_ID=$(echo "$EXISTING_APPLICATION" | ${pkgs.jq}/bin/jq -r '.id') |
| 97 | +
|
| 98 | + UPDATED_APPLICATION=$(apply_field_overrides "$EXISTING_APPLICATION" "$APPLICATION_API_KEY" "$FIELD_OVERRIDES") |
| 99 | +
|
| 100 | + ${pkgs.curl}/bin/curl -sSf -X PUT \ |
| 101 | + -H "X-Api-Key: $API_KEY" \ |
| 102 | + -H "Content-Type: application/json" \ |
| 103 | + -d "$UPDATED_APPLICATION" \ |
| 104 | + "$BASE_URL/applications/$APPLICATION_ID" >/dev/null |
| 105 | +
|
| 106 | + echo "Application ${applicationName} updated" |
| 107 | + else |
| 108 | + echo "Application ${applicationName} does not exist, creating..." |
| 109 | +
|
| 110 | + SCHEMA=$(echo "$SCHEMAS" | ${pkgs.jq}/bin/jq -r '.[] | select(.implementationName == "${implementationName}") | @json' || echo "") |
| 111 | +
|
| 112 | + if [ -z "$SCHEMA" ]; then |
| 113 | + echo "Error: No schema found for application implementationName ${implementationName}" |
| 114 | + exit 1 |
| 115 | + fi |
| 116 | +
|
| 117 | + NEW_APPLICATION=$(apply_field_overrides "$SCHEMA" "$APPLICATION_API_KEY" "$FIELD_OVERRIDES") |
| 118 | +
|
| 119 | + ${pkgs.curl}/bin/curl -sSf -X POST \ |
| 120 | + -H "X-Api-Key: $API_KEY" \ |
| 121 | + -H "Content-Type: application/json" \ |
| 122 | + -d "$NEW_APPLICATION" \ |
| 123 | + "$BASE_URL/applications" >/dev/null |
| 124 | +
|
| 125 | + echo "Application ${applicationName} created" |
| 126 | + fi |
| 127 | + '') |
| 128 | + serviceConfig.applications} |
| 129 | +
|
| 130 | + echo "${capitalizedName} applications configuration complete" |
| 131 | + ''; |
| 132 | +} |
0 commit comments