454 lines
14 KiB
JSON
454 lines
14 KiB
JSON
{
|
||
"id": "aS3YcO4RhtvsdH6q",
|
||
"name": "transmission session mgmt",
|
||
"nodes": [
|
||
{
|
||
"parameters": {},
|
||
"type": "n8n-nodes-base.manualTrigger",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
-832,
|
||
-256
|
||
],
|
||
"id": "6a15d79a-58f3-4bd0-8ad7-d78c3d2e1ef8",
|
||
"name": "When clicking ‘Execute workflow’"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"conditions": {
|
||
"options": {
|
||
"caseSensitive": true,
|
||
"leftValue": "",
|
||
"typeValidation": "strict"
|
||
},
|
||
"conditions": [
|
||
{
|
||
"id": "condition-1",
|
||
"leftValue": "={{ $json.exists_in_db }}",
|
||
"rightValue": true,
|
||
"operator": {
|
||
"type": "boolean",
|
||
"operation": "equals"
|
||
}
|
||
}
|
||
],
|
||
"combinator": "and"
|
||
},
|
||
"options": {}
|
||
},
|
||
"id": "if-exists-branch",
|
||
"name": "Record Exists?",
|
||
"type": "n8n-nodes-base.if",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
1184,
|
||
-160
|
||
]
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "// Configure all Transmission servers to manage\n// Add or remove server URLs as needed\n// Credentials are managed by n8n and applied to HTTP requests\n\nconst servers = [\n 'http://seed-1.dfw.ben.io:9091/transmission/rpc',\n 'http://seed-1.rdu.ben.io:9091/transmission/rpc',\n 'http://seed-1.lax.ben.io:9091/transmission/rpc',\n // Add more servers here:\n // 'http://seed-2.dfw.ben.io:9091/transmission/rpc',\n];\n\nreturn servers.map(url => ({\n json: { url }\n}));"
|
||
},
|
||
"id": "configure-servers-code",
|
||
"name": "Configure Servers",
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
-608,
|
||
-160
|
||
]
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "// Match each server with its existing session (if any)\n// Takes servers from Configure Servers and sessions from Get All Stored Sessions\n\nconst servers = $('Configure Servers').all();\nconst sessions = $('Get All Stored Sessions').all();\n\n// Create a lookup map of server_url -> session data\nconst sessionMap = {};\nfor (const session of sessions) {\n sessionMap[session.json.server_url] = session.json;\n}\n\n// Match each server with its session\nconst results = [];\nfor (const server of servers) {\n const url = server.json.url;\n const existingSession = sessionMap[url];\n \n results.push({\n json: {\n url: url,\n session_id: existingSession?.session_id || null,\n has_session: !!existingSession\n }\n });\n}\n\nreturn results;"
|
||
},
|
||
"id": "match-servers-sessions",
|
||
"name": "Match Servers with Sessions",
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
-160,
|
||
-160
|
||
]
|
||
},
|
||
{
|
||
"parameters": {
|
||
"conditions": {
|
||
"options": {
|
||
"caseSensitive": true,
|
||
"leftValue": "",
|
||
"typeValidation": "strict",
|
||
"version": 1
|
||
},
|
||
"conditions": [
|
||
{
|
||
"id": "condition-1",
|
||
"leftValue": "={{ $json.has_session }}",
|
||
"rightValue": true,
|
||
"operator": {
|
||
"type": "boolean",
|
||
"operation": "equals"
|
||
}
|
||
}
|
||
],
|
||
"combinator": "and"
|
||
},
|
||
"options": {}
|
||
},
|
||
"id": "has-existing-session-if",
|
||
"name": "Has Existing Session?",
|
||
"type": "n8n-nodes-base.if",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
64,
|
||
-160
|
||
]
|
||
},
|
||
{
|
||
"parameters": {
|
||
"method": "POST",
|
||
"url": "={{ $json.url }}",
|
||
"authentication": "genericCredentialType",
|
||
"genericAuthType": "httpBasicAuth",
|
||
"sendHeaders": true,
|
||
"headerParameters": {
|
||
"parameters": [
|
||
{
|
||
"name": "X-Transmission-Session-Id",
|
||
"value": "={{ $json.session_id }}"
|
||
}
|
||
]
|
||
},
|
||
"sendBody": true,
|
||
"specifyBody": "json",
|
||
"jsonBody": "{\"method\": \"session-get\"}",
|
||
"options": {
|
||
"response": {
|
||
"response": {
|
||
"fullResponse": true,
|
||
"neverError": true
|
||
}
|
||
},
|
||
"timeout": 5000
|
||
}
|
||
},
|
||
"id": "validate-session-http",
|
||
"name": "Validate Session",
|
||
"type": "n8n-nodes-base.httpRequest",
|
||
"typeVersion": 4.2,
|
||
"position": [
|
||
288,
|
||
-240
|
||
],
|
||
"credentials": {
|
||
"httpBasicAuth": {
|
||
"id": "iymUPilnVhfL3h5D",
|
||
"name": "transmission"
|
||
}
|
||
},
|
||
"onError": "continueErrorOutput"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"method": "POST",
|
||
"url": "={{ $json.url }}",
|
||
"authentication": "genericCredentialType",
|
||
"genericAuthType": "httpBasicAuth",
|
||
"options": {
|
||
"response": {
|
||
"response": {
|
||
"fullResponse": true,
|
||
"neverError": true
|
||
}
|
||
},
|
||
"timeout": 10000
|
||
}
|
||
},
|
||
"id": "get-new-session-http",
|
||
"name": "Get New Session ID",
|
||
"type": "n8n-nodes-base.httpRequest",
|
||
"typeVersion": 4.2,
|
||
"position": [
|
||
512,
|
||
-160
|
||
],
|
||
"credentials": {
|
||
"httpBasicAuth": {
|
||
"id": "iymUPilnVhfL3h5D",
|
||
"name": "transmission"
|
||
}
|
||
},
|
||
"onError": "continueErrorOutput"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "// Extract the X-Transmission-Session-Id from the 409 error response\n// Transmission returns a 409 error with the session ID in the HTML body\n\nconst items = $input.all();\nconst results = [];\n\n// Get all items from the Get New Session ID node to preserve the URL\nconst httpRequestItems = $('Get New Session ID').all();\n\nfor (let i = 0; i < items.length; i++) {\n const item = items[i];\n let sessionId = null;\n \n // Get the server URL from the corresponding HTTP request input\n let serverUrl = httpRequestItems[i]?.json?.url;\n \n if (!serverUrl) {\n // Fallback: try to get from Match Servers node\n const matchedItems = $('Match Servers with Sessions').all();\n if (matchedItems[i]?.json?.url) {\n serverUrl = matchedItems[i].json.url;\n } else {\n throw new Error('Could not determine server URL from workflow context');\n }\n }\n \n // Try to get session ID from headers first (if available)\n if (item.json?.headers?.['x-transmission-session-id']) {\n sessionId = item.json.headers['x-transmission-session-id'];\n }\n // Check if it's in the response body directly\n else if (item.json?.body && typeof item.json.body === 'string') {\n const match = item.json.body.match(/X-Transmission-Session-Id:\\s*([A-Za-z0-9]+)/i);\n if (match && match[1]) {\n sessionId = match[1];\n }\n }\n // Try to extract from error message HTML\n else if (item.json?.error?.message) {\n const errorMessage = item.json.error.message;\n const match = errorMessage.match(/X-Transmission-Session-Id:\\s*([A-Za-z0-9]+)/i);\n if (match && match[1]) {\n sessionId = match[1];\n }\n }\n // Check error object directly\n else if (item.error?.message) {\n const errorMessage = item.error.message;\n const match = errorMessage.match(/X-Transmission-Session-Id:\\s*([A-Za-z0-9]+)/i);\n if (match && match[1]) {\n sessionId = match[1];\n }\n }\n \n if (sessionId) {\n results.push({\n json: {\n session_id: sessionId,\n server_url: serverUrl\n }\n });\n } else {\n // Debug: Show what data we received\n const debugInfo = {\n hasHeaders: !!item.json?.headers,\n hasBody: !!item.json?.body,\n hasError: !!item.json?.error,\n hasErrorDirect: !!item.error,\n bodyType: typeof item.json?.body,\n statusCode: item.json?.statusCode,\n keys: Object.keys(item.json || {})\n };\n throw new Error(`Could not extract session ID from response for server: ${serverUrl}. Debug: ${JSON.stringify(debugInfo)}`);\n }\n}\n\nreturn results;"
|
||
},
|
||
"id": "extract-new-session-code",
|
||
"name": "Extract Session ID",
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
736,
|
||
-160
|
||
]
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "// Check if each session already exists in the database\n// This preserves ALL items and adds a database_record field\n\nconst items = $input.all();\nconst allStoredSessions = $('Get All Stored Sessions').all();\n\n// Create a lookup map of server_url -> database record\nconst dbSessionMap = {};\nfor (const session of allStoredSessions) {\n dbSessionMap[session.json.server_url] = session.json;\n}\n\nconst results = [];\nfor (const item of items) {\n const serverUrl = item.json.server_url;\n const dbRecord = dbSessionMap[serverUrl];\n \n results.push({\n json: {\n ...item.json,\n db_record: dbRecord || null,\n exists_in_db: !!dbRecord\n }\n });\n}\n\nreturn results;"
|
||
},
|
||
"id": "check-db-exists-code",
|
||
"name": "Lookup Existing Records",
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
960,
|
||
-160
|
||
]
|
||
},
|
||
{
|
||
"parameters": {
|
||
"rule": {
|
||
"interval": [
|
||
{}
|
||
]
|
||
}
|
||
},
|
||
"type": "n8n-nodes-base.scheduleTrigger",
|
||
"typeVersion": 1.2,
|
||
"position": [
|
||
-832,
|
||
-64
|
||
],
|
||
"id": "d3f1c5f5-2c71-4295-b383-66223212d448",
|
||
"name": "Schedule Trigger"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"operation": "executeQuery",
|
||
"query": "INSERT INTO transmission_sessions (server_url, session_id, last_updated) VALUES ($1, $2, NOW());",
|
||
"additionalFields": {
|
||
"queryParams": "={{ $json.session_id }}, {{ $json.server_url }}"
|
||
}
|
||
},
|
||
"id": "supabase-create",
|
||
"name": "Create New Session (Postgres)",
|
||
"type": "n8n-nodes-base.postgres",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
1408,
|
||
-64
|
||
],
|
||
"credentials": {
|
||
"postgres": {
|
||
"id": "9grzZwW7Br6SzdV8",
|
||
"name": "n8n-media"
|
||
}
|
||
}
|
||
},
|
||
{
|
||
"parameters": {
|
||
"operation": "executeQuery",
|
||
"query": "UPDATE transmission_sessions SET session_id = $1, last_updated = NOW() WHERE server_url = $2;",
|
||
"additionalFields": {
|
||
"queryParams": "={{ $json.session_id }}, {{ $json.server_url }}"
|
||
}
|
||
},
|
||
"id": "supabase-update",
|
||
"name": "Update Existing Session (Postgres)",
|
||
"type": "n8n-nodes-base.postgres",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
1408,
|
||
-256
|
||
],
|
||
"credentials": {
|
||
"postgres": {
|
||
"id": "9grzZwW7Br6SzdV8",
|
||
"name": "n8n-media"
|
||
}
|
||
}
|
||
},
|
||
{
|
||
"parameters": {
|
||
"operation": "executeQuery",
|
||
"query": "SELECT * FROM transmission_sessions;",
|
||
"additionalFields": {}
|
||
},
|
||
"id": "get-all-sessions",
|
||
"name": "Get All Stored Sessions",
|
||
"type": "n8n-nodes-base.postgres",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
-384,
|
||
-160
|
||
],
|
||
"credentials": {
|
||
"postgres": {
|
||
"id": "9grzZwW7Br6SzdV8",
|
||
"name": "n8n-media"
|
||
}
|
||
}
|
||
}
|
||
],
|
||
"connections": {
|
||
"When clicking ‘Execute workflow’": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Configure Servers",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Match Servers with Sessions": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Has Existing Session?",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Has Existing Session?": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Validate Session",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Get New Session ID",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Get New Session ID": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Extract Session ID",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Extract Session ID",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Extract Session ID": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Lookup Existing Records",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Lookup Existing Records": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Record Exists?",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Validate Session": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Get New Session ID",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[]
|
||
]
|
||
},
|
||
"Schedule Trigger": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Configure Servers",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Configure Servers": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Get All Stored Sessions",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Record Exists?": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Update Existing Session (Postgres)",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Create New Session (Postgres)",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Get All Stored Sessions": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Match Servers with Sessions",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
}
|
||
},
|
||
"settings": {
|
||
"executionOrder": "v1",
|
||
"callerPolicy": "workflowsFromSameOwner",
|
||
"availableInMCP": false
|
||
},
|
||
"triggerCount": 0,
|
||
"versionId": "05f3ebfa-af37-4ebb-a75d-755f54238d2e",
|
||
"owner": {
|
||
"type": "personal",
|
||
"projectId": "FeLO36wNUAcn61Wj",
|
||
"projectName": "Ben W <admin@ben.io>",
|
||
"personalEmail": "admin@ben.io"
|
||
},
|
||
"parentFolderId": "kUg4HIPXraph3M0E",
|
||
"isArchived": false
|
||
} |