{ "kind": "open-flow-workflow", "version": 1, "name": "Restore n8n Workflows from GitHub", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_bcc75b9c786429f5": { "name": "Enter the backup directory", "kind": "start", "inputDefs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "owner" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "repo" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "path" } ] }, "n_43257efa083e3278": { "inputs": { "owner": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_bcc75b9c786429f5", "output": "owner" } ] }, "repo": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_bcc75b9c786429f5", "output": "repo" } ] }, "path": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_bcc75b9c786429f5", "output": "path" } ] } }, "name": "Validate the repository", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "owner" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "repo" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "path" } ], "outputs": [ { "jsonSchema": { "type": "string", "minLength": 1, "description": "The repository owner." }, "nullable": false, "handle": "owner" }, { "jsonSchema": { "type": "string", "minLength": 1, "description": "The repository name." }, "nullable": false, "handle": "repo" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "path" } ], "name": "Validate the repository", "moduleId": "n_340e42d43e3eae26", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_4e4aa545356bb481": { "inputs": { "previous": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_4e4aa545356bb481", "output": "state" } ] } }, "name": "Read existing workflows", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object", "properties": { "items": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "cursor": { "type": [ "string", "null" ] }, "seen": { "type": "array", "items": { "type": "string" } } }, "required": [ "items", "cursor", "seen" ], "additionalProperties": false }, "nullable": true, "handle": "previous" } ], "outputs": [ { "jsonSchema": { "type": "object", "properties": { "items": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "cursor": { "type": [ "string", "null" ] }, "seen": { "type": "array", "items": { "type": "string" } } }, "required": [ "items", "cursor", "seen" ], "additionalProperties": false }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" } ], "name": "Read existing workflows", "moduleId": "n_86a08463b83efd8a", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [ { "action": "n8n.list_workflows" } ] } ] } }, "n_7daa96353d5b7b64": { "inputs": {}, "name": "More workflow pages?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "more", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_4e4aa545356bb481", "output": "more" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_7c1b74c1e1f22335": { "inputs": { "owner": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_43257efa083e3278", "output": "owner" } ] }, "repo": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_43257efa083e3278", "output": "repo" } ] }, "path": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_43257efa083e3278", "output": "path" } ] } }, "name": "List the backup directory", "kind": "task", "task": { "inputs": [ { "description": "The repository owner.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The repository owner." }, "nullable": false, "handle": "owner" }, { "description": "The repository name.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The repository name." }, "nullable": false, "handle": "repo" }, { "description": "The directory path.", "jsonSchema": { "type": "string", "description": "The directory path." }, "nullable": true, "handle": "path", "value": null }, { "description": "The branch, tag, or commit SHA.", "jsonSchema": { "type": "string", "description": "The branch, tag, or commit SHA." }, "nullable": true, "handle": "ref", "value": null } ], "outputs": [ { "description": "The directory entries.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "type": { "type": "string", "enum": [ "file", "dir", "symlink", "submodule" ], "description": "The content entry type." }, "name": { "type": "string", "description": "The file or directory name." }, "path": { "type": "string", "description": "The file path." }, "sha": { "type": "string", "description": "The content SHA." }, "size": { "type": "integer", "description": "The file size in bytes." }, "html_url": { "type": [ "string", "null" ], "description": "The content URL." }, "download_url": { "type": [ "string", "null" ], "description": "The download URL." } }, "required": [ "type", "name", "path", "sha" ], "additionalProperties": true, "description": "A GitHub content entry record." }, "description": "The directory entries." }, "nullable": false, "handle": "entries" } ], "name": "List the backup directory", "executor": { "kind": "connector", "action": "github.list_directory_contents" } } }, "n_913ddb8125b7c67e": { "inputs": { "entries": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_7c1b74c1e1f22335", "output": "entries" } ] }, "existing": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_4e4aa545356bb481", "output": "state" } ] }, "owner": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_43257efa083e3278", "output": "owner" } ] }, "repo": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_43257efa083e3278", "output": "repo" } ] }, "previous": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_913ddb8125b7c67e", "output": "state" } ] } }, "name": "Read and validate backups", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "entries" }, { "jsonSchema": { "type": "object", "properties": { "items": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "cursor": { "type": [ "string", "null" ] }, "seen": { "type": "array", "items": { "type": "string" } } }, "required": [ "items", "cursor", "seen" ], "additionalProperties": false }, "nullable": false, "handle": "existing" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "owner" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "repo" }, { "jsonSchema": { "type": "object", "properties": { "index": { "type": "integer" }, "items": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "seen": { "type": "array", "items": { "type": "string" } } }, "required": [ "index", "items", "seen" ], "additionalProperties": false }, "nullable": true, "handle": "previous" } ], "outputs": [ { "jsonSchema": { "type": "object", "properties": { "index": { "type": "integer" }, "items": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "seen": { "type": "array", "items": { "type": "string" } } }, "required": [ "index", "items", "seen" ], "additionalProperties": false }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" } ], "name": "Read and validate backups", "moduleId": "n_e8c393915a445329", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [ { "action": "github.get_file_contents" } ] } ] } }, "n_8955b53b65dc71b9": { "inputs": {}, "name": "More backup files?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "more", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_913ddb8125b7c67e", "output": "more" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_06d5264c8b49b440": { "inputs": { "state": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_913ddb8125b7c67e", "output": "state" } ] }, "previous": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_06d5264c8b49b440", "output": "state" } ] } }, "name": "Prepare the next restore", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object", "properties": { "index": { "type": "integer" }, "items": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "seen": { "type": "array", "items": { "type": "string" } } }, "required": [ "index", "items", "seen" ], "additionalProperties": false }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previous" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "body" } ], "name": "Prepare the next restore", "moduleId": "n_70099d66d23c7e48", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_8316fb6fa99bcc89": { "inputs": {}, "name": "More workflows to restore?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "more", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_06d5264c8b49b440", "output": "more" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_347f86635e64565f": { "inputs": { "endpoint": { "kind": "value", "value": "/workflows" }, "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_06d5264c8b49b440", "output": "body" } ] } }, "name": "Create the workflow", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "endpoint" }, { "jsonSchema": {}, "nullable": true, "handle": "body" } ], "outputs": [ { "jsonSchema": {}, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "statusCode" }, { "jsonSchema": { "type": "object", "additionalProperties": { "type": "string" } }, "nullable": false, "handle": "headers" }, { "jsonSchema": { "const": "base64" }, "nullable": true, "handle": "bodyEncoding" } ], "name": "Create the workflow", "executor": { "providerId": "n8n", "method": "POST", "query": { "mode": "fields", "names": [] }, "headers": { "mode": "fields", "names": [] }, "bodyFormat": "json", "kind": "connector-proxy" } } }, "n_c51379b251505ea0": { "inputs": { "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_347f86635e64565f", "output": "body" } ] }, "statusCode": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_347f86635e64565f", "output": "statusCode" } ] }, "bodyEncoding": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_347f86635e64565f", "output": "bodyEncoding" } ] }, "expected": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_06d5264c8b49b440", "output": "body" } ] } }, "name": "Confirm the created workflow", "kind": "task", "task": { "inputs": [ { "jsonSchema": {}, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "statusCode" }, { "jsonSchema": { "const": "base64" }, "nullable": true, "handle": "bodyEncoding" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "expected" } ], "outputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "id" } ], "name": "Confirm the created workflow", "moduleId": "n_65a9621a69548c30", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } } }, "edges": [ { "source": "n_bcc75b9c786429f5", "target": "n_43257efa083e3278" }, { "source": "n_43257efa083e3278", "target": "n_4e4aa545356bb481" }, { "source": "n_4e4aa545356bb481", "target": "n_7daa96353d5b7b64" }, { "source": "n_7daa96353d5b7b64", "target": "n_4e4aa545356bb481", "sourceHandle": "more" }, { "source": "n_7daa96353d5b7b64", "target": "n_7c1b74c1e1f22335", "sourceHandle": "otherwise" }, { "source": "n_7c1b74c1e1f22335", "target": "n_913ddb8125b7c67e" }, { "source": "n_913ddb8125b7c67e", "target": "n_8955b53b65dc71b9" }, { "source": "n_8955b53b65dc71b9", "target": "n_913ddb8125b7c67e", "sourceHandle": "more" }, { "source": "n_8955b53b65dc71b9", "target": "n_06d5264c8b49b440", "sourceHandle": "otherwise" }, { "source": "n_06d5264c8b49b440", "target": "n_8316fb6fa99bcc89" }, { "source": "n_8316fb6fa99bcc89", "target": "n_347f86635e64565f", "sourceHandle": "more" }, { "source": "n_347f86635e64565f", "target": "n_c51379b251505ea0" }, { "source": "n_c51379b251505ea0", "target": "n_06d5264c8b49b440" } ] } }, "modules": { "n_340e42d43e3eae26": { "name": "Validate the repository", "imports": [], "source": "export default function config({owner,repo,path}){for(const [k,v] of Object.entries({owner,repo,path}))if(typeof v!=='string'||!v.trim()||v.startsWith('YOUR_'))throw Error('Configure GitHub '+k);if(path.startsWith('/')||path.endsWith('/')||path.split('/').some(x=>!x||x==='.'||x==='..'))throw Error('Invalid existing directory');return{owner,repo,path}}\n" }, "n_86a08463b83efd8a": { "name": "Read existing workflows", "imports": [], "source": "export default async function pages({previous},context){const s=previous??{items:[],cursor:null,seen:[]};const r=await context.actions.call('n8n.list_workflows',{limit:100,...(s.cursor?{cursor:s.cursor}:{})});if(!Array.isArray(r?.data))throw Error('Invalid n8n workflow page');const c=r.nextCursor;if(c!==null&&(typeof c!=='string'||!c||s.seen.includes(c)))throw Error('Invalid or repeated workflow cursor');return {state:{items:s.items.concat(r.data),cursor:c,seen:c?[...s.seen,c]:s.seen},more:c!==null}}\n" }, "n_e8c393915a445329": { "name": "Read and validate backups", "imports": [], "source": "export default async function files({entries,existing,owner,repo,previous},context){if(!Array.isArray(entries)||!Array.isArray(existing.items))throw Error('Invalid lists');if(entries.length>=1000)throw Error('Directory may exceed GitHub listing limit');const s=previous??{index:0,items:[],seen:[]};if(s.index>=entries.length)return{state:s,more:false};const e=entries[s.index];if(e.type!=='file'||typeof e.name!=='string'||!e.name.endsWith('.json')||typeof e.path!=='string')throw Error('Directory must contain workflow JSON files only');const f=await context.actions.call('github.get_file_contents',{owner,repo,path:e.path});if(f.type!=='file'||f.path!==e.path||typeof f.decoded_content!=='string')throw Error('Invalid workflow file');let w;try{w=JSON.parse(f.decoded_content)}catch{throw Error('Invalid workflow JSON')}if(!w||typeof w.name!=='string'||!w.name.trim()||!Array.isArray(w.nodes)||!w.connections||typeof w.connections!=='object'||!w.settings||typeof w.settings!=='object')throw Error('Invalid workflow backup');if(s.seen.includes(w.name))throw Error('Duplicate backup workflow name');const payload={name:w.name,nodes:w.nodes,connections:w.connections,settings:w.settings,...(Object.hasOwn(w,'staticData')?{staticData:w.staticData}:{})};const items=existing.items.some(x=>x.name===w.name)?s.items:s.items.concat(payload);return{state:{index:s.index+1,items,seen:[...s.seen,w.name]},more:s.index+1=300||bodyEncoding||!body||typeof body.id!=='string'||!body.id||body.name!==expected.name)throw Error('Workflow creation was not confirmed');return {id:body.id}}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }