10 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | must_haves | |||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 16-api-migration | 02 | execute | 1 |
|
true |
|
Purpose: Container lifecycle actions are the second-most-used feature. This plan replaces 4 Docker API HTTP Request nodes (1 container list + 3 actions) with GraphQL equivalents, using Container ID Registry for name-to-PrefixedID translation and GraphQL Error Handler for ALREADY_IN_STATE mapping.
Output: n8n-actions.json with all Docker API nodes replaced by Unraid GraphQL mutations, restart implemented as sequential stop+start (no native restart mutation), error handling preserving existing statusCode 304 pattern.
<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/16-api-migration/16-RESEARCH.md @.planning/phases/15-infrastructure-foundation/15-01-SUMMARY.md @.planning/phases/15-infrastructure-foundation/15-02-SUMMARY.md @n8n-actions.json @n8n-workflow.json (for Phase 15 utility node code — Container ID Registry, GraphQL Error Handler, HTTP Template) @ARCHITECTURE.md Task 1: Replace container list query and resolve with Container ID Registry n8n-actions.json Replace the container lookup flow in n8n-actions.json. Currently: - "Has Container ID?" IF node → "Get All Containers" HTTP Request → "Resolve Container ID" Code nodeThe current flow fetches ALL containers from Docker API, then searches by name in Code node to find the container ID. Replace with Unraid GraphQL query + Container ID Registry:
-
Replace "Get All Containers" (GET docker-socket-proxy:2375/v1.47/containers/json?all=true):
- Change to: POST
={{ $env.UNRAID_HOST }}/graphql - Body:
{"query": "query { docker { containers { id names state image } } }"} - Headers:
Content-Type: application/json,x-api-key: ={{ $env.UNRAID_API_KEY }} - Timeout: 15000ms, error handling:
continueRegularOutput - Rename to "Query All Containers"
- Change to: POST
-
Add GraphQL Response Normalizer Code node after the HTTP Request (before Resolve Container ID). Copy normalizer logic from n8n-workflow.json utility node. This transforms GraphQL response to Docker API contract format so "Resolve Container ID" Code node works unchanged.
-
Add Container ID Registry update after normalizer — a Code node that updates the static data registry with fresh name→PrefixedID mappings. This is critical because downstream mutations need PrefixedIDs.
-
Update "Resolve Container ID" Code node: After normalization, this node already works (it searches by
Names[0]). However, enhance it to also output theunraidId(PrefixedID) from theIdfield, so downstream mutation nodes can use it directly. Add to the output:unraidId: matched.Id(the normalizer preserves the full PrefixedID in theIdfield).
Wire: Has Container ID? (false) → Query All Containers → Normalizer → Registry Update → Resolve Container ID → Route Action Load n8n-actions.json and verify:
- "Get All Containers" node replaced with GraphQL query
- Normalizer Code node exists between HTTP Request and Resolve Container ID
- Resolve Container ID outputs unraidId field
- No "docker-socket-proxy" in any URL Container lookup uses Unraid GraphQL API with normalizer. Container ID Registry updated on every lookup. Resolve Container ID outputs unraidId (PrefixedID) for downstream mutations.
-
Replace "Start Container" (POST docker-socket-proxy:2375/v1.47/containers/{id}/start):
- Add a "Build Start Mutation" Code node before the HTTP Request that constructs the GraphQL mutation body:
const data = $('Route Action').item.json; const unraidId = data.unraidId || data.containerId; return { json: { query: `mutation { docker { start(id: "${unraidId}") { id state } } }` } }; - Change HTTP Request to: POST
={{ $env.UNRAID_HOST }}/graphql, body from expression={{ JSON.stringify({query: $json.query}) }} - Headers:
Content-Type: application/json,x-api-key: ={{ $env.UNRAID_API_KEY }} - Timeout: 15000ms, error handling:
continueRegularOutput - Add GraphQL Error Handler Code node after HTTP Request (before Format Start Result). Copy error handler logic from n8n-workflow.json utility node. Maps
ALREADY_IN_STATE→{statusCode: 304},NOT_FOUND→{statusCode: 404}. - Wire: Route Action → Build Start Mutation → Start Container (HTTP) → Error Handler → Format Start Result
- Add a "Build Start Mutation" Code node before the HTTP Request that constructs the GraphQL mutation body:
-
Replace "Stop Container" (POST docker-socket-proxy:2375/v1.47/containers/{id}/stop?t=10):
- Same pattern as Start: Build Stop Mutation → HTTP Request → Error Handler → Format Stop Result
- Mutation:
mutation { docker { stop(id: "${unraidId}") { id state } } } - Timeout: 15000ms
-
Replace "Restart Container" (POST docker-socket-proxy:2375/v1.47/containers/{id}/restart?t=10): Unraid has NO native restart mutation. Implement as sequential stop + start:
a. Build Stop-for-Restart Mutation Code node:
const data = $('Route Action').item.json; const unraidId = data.unraidId || data.containerId; return { json: { query: `mutation { docker { stop(id: "${unraidId}") { id state } } }`, unraidId } };b. Stop For Restart HTTP Request node (same config as Stop Container) c. Handle Stop-for-Restart Result Code node:
- Check response: if success OR statusCode 304 (already stopped) → proceed to start
- If error → fail restart
const response = $input.item.json; const prevData = $('Build Stop-for-Restart Mutation').item.json; if (response.statusCode && response.statusCode !== 304 && !response.data) { return { json: { error: true, statusCode: response.statusCode, message: 'Failed to stop container for restart' } }; } return { json: { query: `mutation { docker { start(id: "${prevData.unraidId}") { id state } } }` } };d. Start After Stop HTTP Request node (same config as Start Container) e. Restart Error Handler Code node (same GraphQL Error Handler logic) f. Wire: Route Action → Build Stop-for-Restart → Stop For Restart (HTTP) → Handle Stop-for-Restart → Start After Stop (HTTP) → Restart Error Handler → Format Restart Result
Critical: The existing "Format Restart Result" Code node checks
response.statusCode === 304which means "already running". For restart, 304 on the start step would mean the container didn't actually stop then start — it was already running. This is correct behavior for the existing Format Restart Result node.
Existing Format Start/Stop/Restart Result Code nodes remain UNCHANGED. They already check:
response.statusCode === 304→ "already in desired state"!response.message && !response.error→ success (Docker 204 No Content pattern)- The GraphQL Error Handler output maps to match these exact patterns.
Rename Docker-centric HTTP Request nodes:
- "Start Container" → "Start Container" (keep name, just change URL/method)
- "Stop Container" → "Stop Container" (keep name)
- Remove old "Restart Container" single-node and replace with stop+start chain Load n8n-actions.json and verify:
- Zero "docker-socket-proxy" references in any node URL
- Start and Stop nodes use POST to
$env.UNRAID_HOST/graphqlwith mutation bodies - Restart implemented as 2 HTTP Request nodes (stop then start) with intermediate error handling
- GraphQL Error Handler Code nodes exist after each mutation HTTP Request
- Format Start/Stop/Restart Result Code nodes are UNCHANGED from pre-migration
- All connections valid
- Push to n8n via API and verify HTTP 200 Container start/stop use single GraphQL mutations. Restart uses sequential stop+start with ALREADY_IN_STATE tolerance on stop step. Error Handler maps GraphQL errors to statusCode 304 pattern. Format Result nodes unchanged. Workflow pushed to n8n.
<success_criteria>
- n8n-actions.json has zero Docker socket proxy references
- Start/stop operations use GraphQL mutations with Error Handler
- Restart operates as sequential stop+start with ALREADY_IN_STATE tolerance
- Format Result Code nodes unchanged (zero-change migration for output formatting)
- Container ID Registry updated on container lookup
- Workflow valid and pushed to n8n </success_criteria>