11 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, user_setup, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | user_setup | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 14-unraid-api-access | 01 | execute | 1 |
|
true |
|
|
Purpose: Establishes the foundation for Unraid API connectivity — credential storage mirroring the existing .env.n8n-api pattern, gitignore protection, and actual n8n workflow nodes that query the Unraid GraphQL API to validate connectivity, authentication, and container data structure.
Output: .env.unraid-api template file, updated .gitignore, and new nodes in n8n-workflow.json for Unraid API testing.
<execution_context> @/home/luc/.claude/get-shit-done/workflows/execute-plan.md @/home/luc/.claude/get-shit-done/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/14-unraid-api-access/14-CONTEXT.md @.planning/phases/14-unraid-api-access/14-RESEARCH.md @ARCHITECTURE.md @CLAUDE.md @.gitignore @n8n-workflow.json Task 1: Create credential infrastructure .env.unraid-api, .gitignore 1. Create `.env.unraid-api` file with template values, mirroring the existing `.env.n8n-api` pattern: ``` UNRAID_HOST=http://host.docker.internal UNRAID_API_KEY=your-unraid-api-key-here ```-
Add
.env.unraid-apito.gitignoreon a new line after the existing.env.n8n-apientry. Keep the comment header relevant (update if needed, e.g., "Environment files with sensitive credentials"). -
Update
CLAUDE.md— add a new section "## Unraid API Access" (after the existing "## n8n API Access" section) documenting:- Credential file location and structure (
.env.unraid-api) - Loading pattern:
. .env.unraid-api; curl -X POST "${UNRAID_HOST}/graphql" ... - GraphQL query pattern for testing
- Note that this mirrors the n8n API access pattern
- Credential file location and structure (
Per user decision: Dual storage approach — .env.unraid-api for CLI/deploy scripts, n8n Header Auth credential for workflow nodes. The n8n credential creation is a user setup step (documented in plan's user_setup frontmatter), not automated here.
.env.unraid-apiexists withUNRAID_HOSTandUNRAID_API_KEYvariablesgrep -q ".env.unraid-api" .gitignorereturns 0CLAUDE.mdcontains "Unraid API Access" section Credential template file exists with correct variables, is gitignored, and usage is documented in CLAUDE.md
The branch needs 3 new nodes:
Node 1: HTTP Request node ("Unraid API Test")
- Method: POST
- URL:
={{ $env.UNRAID_HOST }}/graphql(reads from n8n environment variable, OR use expression that can be overridden) - Authentication: Header Auth
- Credential name: "Unraid API Key" (Header Auth type, header name:
x-api-key) - NOTE: The credential must be created manually by the user in n8n. Use credential ID placeholder — the user will need to update this after creating the credential.
- Credential name: "Unraid API Key" (Header Auth type, header name:
- Body (JSON):
{ "query": "query { docker { containers { id names state isUpdateAvailable } } }" } - Options: "Ignore SSL Issues" = true (handles self-signed certs per research pitfall #2)
- On Error: "Continue on Error" (so the Code node can handle errors gracefully)
Node 2: Code node ("Validate Unraid Response")
- Validates the GraphQL response:
const response = $input.item.json; // Check for HTTP errors (from continueOnError) if (response.error) { return { json: { action: 'send_message', text: `❌ <b>Unraid API Error</b>\n\n` + `<b>Error:</b> ${response.error.message || JSON.stringify(response.error)}\n\n` + `<b>Check:</b>\n` + `• Is UNRAID_HOST correct?\n` + `• Is the API key valid?\n` + `• Can n8n reach the Unraid host? (--add-host flag)\n` + `• Is Unraid GraphQL API enabled? (v7.2+ or Connect plugin)`, chatId: $('Telegram Trigger').item.json.message.chat.id } }; } // Check for GraphQL errors if (response.errors) { const errorMsg = response.errors.map(e => e.message).join(', '); return { json: { action: 'send_message', text: `❌ <b>Unraid GraphQL Error</b>\n\n${errorMsg}`, chatId: $('Telegram Trigger').item.json.message.chat.id } }; } // Validate response structure if (!response.data?.docker?.containers) { return { json: { action: 'send_message', text: '❌ <b>Unexpected response</b>\n\nNo container data in GraphQL response.', chatId: $('Telegram Trigger').item.json.message.chat.id } }; } const containers = response.data.docker.containers; const sampleId = containers[0]?.id || 'N/A'; const updateCount = containers.filter(c => c.isUpdateAvailable).length; return { json: { action: 'send_message', text: `✅ <b>Unraid API Connected</b>\n\n` + `<b>Containers:</b> ${containers.length}\n` + `<b>Updates available:</b> ${updateCount}\n` + `<b>ID format:</b> <code>${sampleId}</code>\n\n` + `Sample:\n` + containers.slice(0, 5).map(c => `• ${c.names?.[0] || c.id} (${c.state})${c.isUpdateAvailable ? ' 🔄' : ''}` ).join('\n'), chatId: $('Telegram Trigger').item.json.message.chat.id } }; - Uses clear, descriptive error messages per user decision (what failed, why, what to check)
- Uses existing error logging pattern (structured return, same shape as Docker API errors)
Node 3: Telegram Send Message node ("Send Unraid Test Result")
- Chat ID:
={{ $json.chatId }} - Text:
={{ $json.text }} - Parse Mode: HTML
- Use existing Telegram credential (ID:
I0xTTiASl7C1NZhJ)
Wiring:
- Connect Keyword Router's new "unraid" output -> HTTP Request node -> Code node -> Telegram Send node
- Add "unraid" as a new
containsrule in the Keyword Router Switch node. Place it AFTER existingstartsWithrules but the order amongcontainsrules doesn't matter since "unraid" is unique. - Update the Keyword Router's
rulesarray andconnectionsaccordingly.
Important n8n JSON patterns (from CLAUDE.md):
- Telegram credential:
{ "id": "I0xTTiASl7C1NZhJ", "name": "Telegram account" } - Node IDs: Generate unique UUIDs for each new node
- Position: Place nodes visually below/after existing keyword branches (check existing node positions for Y-offset pattern)
- For the Header Auth credential: Use a placeholder credential reference. The user creates "Unraid API Key" credential in n8n manually and the credential ID gets set. Use name "Unraid API Key" in the node JSON.
Auth check: The "unraid" command goes through the existing Auth IF node (same path as all other text commands), so it's already protected.
python3 -c "import json; wf=json.load(open('n8n-workflow.json')); nodes=[n['name'] for n in wf['nodes']]; print('Unraid API Test' in nodes, 'Validate Unraid Response' in nodes, 'Send Unraid Test Result' in nodes)"All three should print True.- Keyword Router Switch node contains "unraid" rule.
- Connections wire: Keyword Router -> Unraid API Test -> Validate Unraid Response -> Send Unraid Test Result.
- Push workflow to n8n:
. .env.n8n-api; ...push recipe from CLAUDE.md. Verify HTTP 200. Main workflow contains a working "unraid" keyword branch with HTTP Request (GraphQL query), validation Code node (error handling with descriptive messages), and Telegram response node. Workflow pushes to n8n successfully.
<success_criteria>
- Credential infrastructure mirrors existing
.env.n8n-apipattern exactly - Test workflow branch is wired into main workflow's existing auth-protected text path
- Error handling provides clear, actionable feedback (what failed, why, what to check)
- Test query includes fields needed by Phase 15+: id, names, state, isUpdateAvailable </success_criteria>