Files
unraid-docker-manager/.planning/phases/14-unraid-api-access/14-01-PLAN.md
T
2026-02-08 20:22:11 -05:00

245 lines
11 KiB
Markdown

---
phase: 14-unraid-api-access
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- .env.unraid-api
- .gitignore
- n8n-workflow.json
autonomous: true
user_setup:
- service: unraid-api
why: "GraphQL API authentication for container update sync"
env_vars:
- name: UNRAID_API_KEY
source: "Unraid WebGUI -> Settings -> Management Access -> API Keys -> Create, or SSH: unraid-api apikey --create --name 'Docker Manager Bot' --permissions 'DOCKER:UPDATE_ANY' --description 'Container update status sync' --json"
- name: UNRAID_HOST
source: "User's Unraid WebGUI URL (e.g., http://192.168.1.100 or http://tower.local). If using host.docker.internal, must add '--add-host=host.docker.internal:host-gateway' to n8n container Extra Parameters in Unraid Docker template editor."
dashboard_config:
- task: "Create API key with DOCKER:UPDATE_ANY permission"
location: "Unraid WebGUI -> Settings -> Management Access -> API Keys, or via SSH"
- task: "Add --add-host=host.docker.internal:host-gateway to n8n container Extra Parameters (if using host.docker.internal)"
location: "Unraid WebGUI -> Docker -> n8n container -> Edit -> Extra Parameters"
must_haves:
truths:
- "Credential template file exists with correct variable names matching .env.n8n-api pattern"
- "Credential file is gitignored and will not be committed"
- "n8n workflow contains HTTP Request node configured for Unraid GraphQL API"
- "n8n workflow contains error handling for GraphQL response validation"
- "Test query requests fields needed by downstream phases (id, names, state, isUpdateAvailable)"
artifacts:
- path: ".env.unraid-api"
provides: "Credential template with UNRAID_HOST and UNRAID_API_KEY"
contains: "UNRAID_HOST"
- path: ".gitignore"
provides: "Gitignore entry for .env.unraid-api"
contains: ".env.unraid-api"
- path: "n8n-workflow.json"
provides: "Unraid GraphQL test query nodes in main workflow"
contains: "Unraid"
key_links:
- from: "n8n-workflow.json (HTTP Request node)"
to: "Unraid GraphQL API"
via: "POST to UNRAID_HOST/graphql with x-api-key header"
pattern: "/graphql"
- from: "n8n-workflow.json (Code node)"
to: "HTTP Request node"
via: "Response validation and error handling"
pattern: "data\\.docker\\.containers"
---
<objective>
Set up credential infrastructure and build Unraid GraphQL API test nodes in the main n8n workflow.
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.
</objective>
<execution_context>
@/home/luc/.claude/get-shit-done/workflows/execute-plan.md
@/home/luc/.claude/get-shit-done/templates/summary.md
</execution_context>
<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
</context>
<tasks>
<task type="auto">
<name>Task 1: Create credential infrastructure</name>
<files>.env.unraid-api, .gitignore</files>
<action>
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
```
2. Add `.env.unraid-api` to `.gitignore` on a new line after the existing `.env.n8n-api` entry. Keep the comment header relevant (update if needed, e.g., "Environment files with sensitive credentials").
3. 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
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.
</action>
<verify>
- `.env.unraid-api` exists with `UNRAID_HOST` and `UNRAID_API_KEY` variables
- `grep -q ".env.unraid-api" .gitignore` returns 0
- `CLAUDE.md` contains "Unraid API Access" section
</verify>
<done>Credential template file exists with correct variables, is gitignored, and usage is documented in CLAUDE.md</done>
</task>
<task type="auto">
<name>Task 2: Add Unraid GraphQL test nodes to main workflow</name>
<files>n8n-workflow.json</files>
<action>
Add a new branch to the main workflow's Keyword Router (Switch node) that handles an "unraid" keyword. This creates a test path: user sends "unraid" -> Keyword Router -> Unraid test nodes -> Telegram response.
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.
- 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:
```javascript
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 `contains` rule in the Keyword Router Switch node. Place it AFTER existing `startsWith` rules but the order among `contains` rules doesn't matter since "unraid" is unique.
- Update the Keyword Router's `rules` array and `connections` accordingly.
**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.
</action>
<verify>
- `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.
</verify>
<done>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.</done>
</task>
</tasks>
<verification>
- `.env.unraid-api` exists with template values
- `.gitignore` includes `.env.unraid-api`
- `CLAUDE.md` documents Unraid API access pattern
- `n8n-workflow.json` contains 3 new nodes: "Unraid API Test", "Validate Unraid Response", "Send Unraid Test Result"
- Keyword Router has "unraid" rule
- All connections properly wired
- Workflow pushes to n8n without errors
</verification>
<success_criteria>
- Credential infrastructure mirrors existing `.env.n8n-api` pattern 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>
<output>
After completion, create `.planning/phases/14-unraid-api-access/14-01-SUMMARY.md`
</output>