245 lines
11 KiB
Markdown
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>
|