From a845abbf2b2c622ab1e6fc0cf0420819843116dc Mon Sep 17 00:00:00 2001 From: gitea Date: Mon, 1 Jan 2001 00:00:00 +0000 Subject: [PATCH] Add Backup workflows to git repository on Gitea.json --- ... workflows to git repository on Gitea.json | 600 ++++++++++++++++++ 1 file changed, 600 insertions(+) create mode 100644 Backup workflows to git repository on Gitea.json diff --git a/Backup workflows to git repository on Gitea.json b/Backup workflows to git repository on Gitea.json new file mode 100644 index 0000000..f1f1595 --- /dev/null +++ b/Backup workflows to git repository on Gitea.json @@ -0,0 +1,600 @@ +{ + "updatedAt": "2025-11-23T08:46:36.000Z", + "createdAt": "2025-11-23T08:34:12.767Z", + "id": "fsaojBpJsioS4FYe", + "name": "Backup workflows to git repository on Gitea", + "active": false, + "isArchived": false, + "nodes": [ + { + "parameters": { + "values": { + "string": [ + { + "name": "repo.url", + "value": "https://gitea.prinz-pilaw.de" + }, + { + "name": "repo.name", + "value": "n8n-workspace" + }, + { + "name": "repo.owner", + "value": "gitea" + } + ] + }, + "options": {} + }, + "id": "b6997dbf-2f79-4c4f-a6cf-6dbb76c547cd", + "name": "Globals", + "type": "n8n-nodes-base.set", + "position": [ + 464, + 96 + ], + "typeVersion": 1 + }, + { + "parameters": { + "filters": {}, + "requestOptions": {} + }, + "id": "d6b7dbc9-8f6c-4922-a725-6f5e2cbf5e50", + "name": "n8n", + "type": "n8n-nodes-base.n8n", + "position": [ + 704, + 96 + ], + "typeVersion": 1 + }, + { + "parameters": { + "rule": { + "interval": [ + { + "field": "minutes", + "minutesInterval": 45 + } + ] + } + }, + "id": "312ab6a1-64e1-4b63-b94b-87619a7eb184", + "name": "Schedule Trigger", + "type": "n8n-nodes-base.scheduleTrigger", + "position": [ + 240, + 96 + ], + "typeVersion": 1.2 + }, + { + "parameters": { + "content": "Workflow changes committed to the repository", + "height": 80 + }, + "id": "5a6a4dd1-d86b-43f2-80df-4b430615f4e9", + "name": "Sticky Note", + "type": "n8n-nodes-base.stickyNote", + "position": [ + 2480, + 160 + ], + "typeVersion": 1 + }, + { + "parameters": { + "content": "Check if there are any changes in the workflow", + "height": 80 + }, + "id": "f342c5df-d6a3-4997-a94e-f6affd4e929f", + "name": "Sticky Note1", + "type": "n8n-nodes-base.stickyNote", + "position": [ + 2112, + 32 + ], + "typeVersion": 1 + }, + { + "parameters": { + "content": "Create a new file for the workflow", + "height": 80 + }, + "id": "0f93a72a-faf6-4177-bffa-7d4088c0bce7", + "name": "Sticky Note2", + "type": "n8n-nodes-base.stickyNote", + "position": [ + 1664, + 432 + ], + "typeVersion": 1 + }, + { + "parameters": { + "content": "Check if file exists in the repository", + "height": 80 + }, + "id": "4f89f7e1-90c6-4b2a-b928-5839ba0e03e3", + "name": "Sticky Note3", + "type": "n8n-nodes-base.stickyNote", + "position": [ + 1152, + 64 + ], + "typeVersion": 1 + }, + { + "parameters": { + "content": "Get all workflows", + "height": 80 + }, + "id": "2939ebe5-a9ce-4103-b44e-336ae2ac5903", + "name": "Sticky Note5", + "type": "n8n-nodes-base.stickyNote", + "position": [ + 640, + 32 + ], + "typeVersion": 1 + }, + { + "parameters": { + "content": "Set variables", + "height": 80 + }, + "id": "1fa6cc74-f22d-4106-84dc-36478abb400d", + "name": "Sticky Note6", + "type": "n8n-nodes-base.stickyNote", + "position": [ + 352, + 32 + ], + "typeVersion": 1 + }, + { + "parameters": { + "assignments": { + "assignments": [ + { + "id": "0a6b769a-c66d-4784-92c7-a70caa28e1ba", + "name": "item", + "type": "object", + "value": "={{ $node[\"ForEach\"].json }}" + } + ] + }, + "options": {} + }, + "id": "b22a1647-7c9f-4dc2-9f9c-44461dece4fc", + "name": "SetDataUpdateNode", + "type": "n8n-nodes-base.set", + "position": [ + 1776, + 96 + ], + "typeVersion": 3.4 + }, + { + "parameters": { + "assignments": { + "assignments": [ + { + "id": "0a6b769a-c66d-4784-92c7-a70caa28e1ba", + "name": "item", + "type": "object", + "value": "={{ $node[\"ForEach\"].json }}" + } + ] + }, + "options": {} + }, + "id": "65a08a09-ee06-4e99-addb-4e94bf32caf2", + "name": "SetDataCreateNode", + "type": "n8n-nodes-base.set", + "position": [ + 1072, + 496 + ], + "typeVersion": 3.4 + }, + { + "parameters": { + "language": "python", + "pythonCode": "import json\nimport base64\nfrom js import Object\n\n# Assuming _input.all() returns a JavaScript object\njs_object = _input.all()\n\n# Convert the JsProxy object to a Python dictionary\ndef js_to_py(js_obj):\n if isinstance(js_obj, (str, int, float, bool)) or js_obj is None:\n # Base types are already Python-compatible\n return js_obj\n elif isinstance(js_obj, list):\n # Convert lists recursively\n return [js_to_py(item) for item in js_obj]\n elif hasattr(js_obj, \"__iter__\") and not isinstance(js_obj, str):\n # Handle JsProxy objects (JavaScript objects or arrays)\n if hasattr(js_obj, \"keys\"):\n # If it has keys, treat it as a dictionary\n return {key: js_to_py(js_obj[key]) for key in Object.keys(js_obj)}\n else:\n # Otherwise, treat it as a list\n return [js_to_py(item) for item in js_obj]\n else:\n # Fallback for other types\n return js_obj\n\n# Convert the JavaScript object to a Python dictionary\ninput_dict = js_to_py(js_object)\n\n# Step 0: get the correct data set of the workflow\ninner_data = input_dict[0].get('json').get('item')\n\n# Step 1: Convert the dictionary to a pretty-printed JSON string\njson_string = json.dumps(inner_data, indent=4)\n\n# Step 2: Encode the JSON string to bytes\njson_bytes = json_string.encode('utf-8')\n\n# Step 3: Convert the bytes to a base64 string\nbase64_string = base64.b64encode(json_bytes).decode('utf-8')\n\n# Step 5: Create the return object with the base64 string and its SHA-256 hash\nreturn_object = {\n \"item\": base64_string\n}\n\n# Return the object\nreturn return_object" + }, + "id": "2d99bcb9-7de7-4db0-a2e3-976754d9034d", + "name": "Base64EncodeUpdate", + "type": "n8n-nodes-base.code", + "position": [ + 2000, + 96 + ], + "typeVersion": 2 + }, + { + "parameters": { + "language": "python", + "pythonCode": "import json\nimport base64\nfrom js import Object\n\n# Assuming _input.all() returns a JavaScript object\njs_object = _input.all()\n\n# Convert the JsProxy object to a Python dictionary\ndef js_to_py(js_obj):\n if isinstance(js_obj, (str, int, float, bool)) or js_obj is None:\n # Base types are already Python-compatible\n return js_obj\n elif isinstance(js_obj, list):\n # Convert lists recursively\n return [js_to_py(item) for item in js_obj]\n elif hasattr(js_obj, \"__iter__\") and not isinstance(js_obj, str):\n # Handle JsProxy objects (JavaScript objects or arrays)\n if hasattr(js_obj, \"keys\"):\n # If it has keys, treat it as a dictionary\n return {key: js_to_py(js_obj[key]) for key in Object.keys(js_obj)}\n else:\n # Otherwise, treat it as a list\n return [js_to_py(item) for item in js_obj]\n else:\n # Fallback for other types\n return js_obj\n\n# Convert the JavaScript object to a Python dictionary\ninput_dict = js_to_py(js_object)\n\n# Step 0: get the correct data set of the workflow\ninner_data = input_dict[0].get('json').get('item')\n\n# Step 1: Convert the dictionary to a pretty-printed JSON string\njson_string = json.dumps(inner_data, indent=4)\n\n# Step 2: Encode the JSON string to bytes\njson_bytes = json_string.encode('utf-8')\n\n# Step 3: Convert the bytes to a base64 string\nbase64_string = base64.b64encode(json_bytes).decode('utf-8')\n\n# Step 4: Create the return object with the base64 string in 'item'\nreturn_object = {\n \"item\": base64_string\n}\n\n# Return the object\nreturn return_object" + }, + "id": "6ff133d8-17bf-4872-92d4-6d9e734a04d5", + "name": "Base64EncodeCreate", + "type": "n8n-nodes-base.code", + "position": [ + 1376, + 496 + ], + "typeVersion": 2 + }, + { + "parameters": { + "conditions": { + "options": { + "version": 2, + "leftValue": "", + "caseSensitive": true, + "typeValidation": "strict" + }, + "combinator": "or", + "conditions": [ + { + "id": "16a9182d-059d-4774-ba95-654fb4293fdb", + "operator": { + "type": "object", + "operation": "notExists", + "singleValue": true + }, + "leftValue": "={{ $json.error }}", + "rightValue": 404 + } + ] + }, + "options": { + "ignoreCase": false + } + }, + "id": "c05cef97-1733-4676-9944-24c9bd8ddeca", + "name": "Exist", + "type": "n8n-nodes-base.if", + "position": [ + 1504, + 112 + ], + "executeOnce": false, + "typeVersion": 2.2, + "alwaysOutputData": false + }, + { + "parameters": { + "conditions": { + "options": { + "version": 2, + "leftValue": "", + "caseSensitive": true, + "typeValidation": "strict" + }, + "combinator": "and", + "conditions": [ + { + "id": "e0c66624-429a-4f1f-bf7b-1cc1b32bad7b", + "operator": { + "type": "string", + "operation": "notEquals" + }, + "leftValue": "={{ $json.item }}", + "rightValue": "={{ $('GetGitea').item.json.content }}" + } + ] + }, + "options": {} + }, + "id": "000af828-3eaf-4b9d-b212-4c69aa1976bc", + "name": "Changed", + "type": "n8n-nodes-base.if", + "position": [ + 2224, + 96 + ], + "typeVersion": 2.2 + }, + { + "parameters": { + "method": "PUT", + "url": "={{ $('Globals').item.json.repo.url }}/api/v1/repos/{{ $('Globals').item.json.repo.owner }}/{{ $('Globals').item.json.repo.name }}/contents/{{ encodeURIComponent($('GetGitea').item.json.name) }}", + "authentication": "genericCredentialType", + "genericAuthType": "httpHeaderAuth", + "sendBody": true, + "bodyParameters": { + "parameters": [ + { + "name": "content", + "value": "={{ $('Base64EncodeUpdate').item.json.item }}" + }, + { + "name": "sha", + "value": "={{ $('GetGitea').item.json.sha }}" + } + ] + }, + "options": {} + }, + "id": "f79dd651-3891-4e9b-b2e8-be5bfa1fe7eb", + "name": "PutGitea", + "type": "n8n-nodes-base.httpRequest", + "position": [ + 2560, + 224 + ], + "typeVersion": 4.2, + "credentials": { + "httpHeaderAuth": { + "id": "NEnkbkj4tAvlTLD8", + "name": "Header Auth account" + } + } + }, + { + "parameters": { + "url": "={{ $('Globals').item.json.repo.url }}/api/v1/repos/{{ encodeURIComponent($('Globals').item.json.repo.owner) }}/{{ encodeURIComponent($('Globals').item.json.repo.name) }}/contents/{{ encodeURIComponent($json.name) }}.json", + "authentication": "genericCredentialType", + "genericAuthType": "httpHeaderAuth", + "options": {} + }, + "id": "4424b8ad-143a-4d3d-b99d-dcee4f02a710", + "name": "GetGitea", + "type": "n8n-nodes-base.httpRequest", + "position": [ + 1232, + 112 + ], + "typeVersion": 4.2, + "credentials": { + "httpHeaderAuth": { + "id": "NEnkbkj4tAvlTLD8", + "name": "Header Auth account" + } + }, + "onError": "continueRegularOutput" + }, + { + "parameters": { + "method": "POST", + "url": "={{ $('Globals').item.json.repo.url }}/api/v1/repos/{{ $('Globals').item.json.repo.owner }}/{{ $('Globals').item.json.repo.name }}/contents/{{ encodeURIComponent($('ForEach').item.json.name) }}.json", + "authentication": "genericCredentialType", + "genericAuthType": "httpHeaderAuth", + "sendBody": true, + "bodyParameters": { + "parameters": [ + { + "name": "content", + "value": "={{ $json.item }}" + } + ] + }, + "options": {} + }, + "id": "fc17d602-92e1-411b-8ba4-00f62046fd2b", + "name": "PostGitea", + "type": "n8n-nodes-base.httpRequest", + "position": [ + 1776, + 496 + ], + "typeVersion": 4.2, + "credentials": { + "httpHeaderAuth": { + "id": "NEnkbkj4tAvlTLD8", + "name": "Header Auth account" + } + } + }, + { + "parameters": { + "options": {} + }, + "id": "ef790cca-0082-408f-bed3-5abf0496082c", + "name": "ForEach", + "type": "n8n-nodes-base.splitInBatches", + "position": [ + 912, + 96 + ], + "executeOnce": false, + "typeVersion": 3 + }, + { + "parameters": { + "content": "### **šŸ“Œ Setup Guide for Backup Workflows to Git Repository on Gitea**\n\n#### **šŸ”§ 1. Configure Global Variables**\nGo to the **Globals** node and update the following:\n- **`repo.url`** → `https://your-gitea-instance.com` *(Replace with your actual Gitea URL)*\n- **`repo.name`** → `workflows` *(Repository name where backups will be stored)*\n- **`repo.owner`** → `octoleo` *(Gitea account that owns the repository)*\n\nšŸ“Œ **These settings define where workflows will be backed up.**\n\n---\n\n#### **šŸ”‘ 2. Set Up Gitea Authentication**\n1ļøāƒ£ **In Gitea:**\n- Generate a **Personal Access Token** under **Settings → Applications → Generate Token**\n- Ensure the token has **repo read/write permissions**\n\n2ļøāƒ£ **In the Credentials Manager:**\n- Create a new **Gitea Token** credential\n- Set the **Name** as `Authorization`\n- Set the **Value** as:\n```\nBearer YOUR_TOKEN_HERE\n```\nšŸ“Œ **Ensure there is a space after `Bearer` before the token!**\n\n---\n\n#### **šŸ”— 3. Connect Gitea Credentials to Git Nodes**\n- Open each of these **three Git nodes**:\n- **GetGitea** → Retrieves existing repository data\n- **PutGitea** → Updates workflows\n- **PostGitea** → Adds new workflows\n\n- Assign the **Gitea Token** credential to each node.\n\nšŸ“Œ **These nodes handle pushing your workflows to Gitea.**\n\n---\n\n#### **🌐 4. Set Up API Credentials for Workflow Retrieval**\n- Locate the API request node that **fetches workflows**.\n- Add your **API authentication credentials** (Token or Basic Auth).\n\nšŸ“Œ **This ensures the workflow can fetch all available workflows from your system.**\n\n---\n\n#### **šŸ› ļø 5. Test & Activate the Workflow**\nāœ… **Run the workflow manually** → Check that workflows are being backed up correctly.\nāœ… **Review the Gitea repository** → Ensure the files are updated.\nāœ… **Enable the scheduled trigger** → Automates backups at defined intervals.\n\nšŸ“Œ **The workflow automatically checks for changes before committing updates!**\n\n---\n\n### **šŸš€ Done! Your Workflows Are Now Backed Up Securely!**\nšŸ’¬ Have issues? **Reach out on the forum for help!**", + "height": 1620, + "width": 560 + }, + "id": "5b242ecc-275e-4d70-861b-64d4b3fcdc82", + "name": "Sticky Note4", + "type": "n8n-nodes-base.stickyNote", + "position": [ + 240, + 304 + ], + "typeVersion": 1 + } + ], + "connections": { + "n8n": { + "main": [ + [ + { + "node": "ForEach", + "type": "main", + "index": 0 + } + ] + ] + }, + "Exist": { + "main": [ + [ + { + "node": "SetDataUpdateNode", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "SetDataCreateNode", + "type": "main", + "index": 0 + } + ] + ] + }, + "Changed": { + "main": [ + [ + { + "node": "PutGitea", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "ForEach", + "type": "main", + "index": 0 + } + ] + ] + }, + "ForEach": { + "main": [ + [], + [ + { + "node": "GetGitea", + "type": "main", + "index": 0 + } + ] + ] + }, + "Globals": { + "main": [ + [ + { + "node": "n8n", + "type": "main", + "index": 0 + } + ] + ] + }, + "GetGitea": { + "main": [ + [ + { + "node": "Exist", + "type": "main", + "index": 0 + } + ] + ] + }, + "PutGitea": { + "main": [ + [ + { + "node": "ForEach", + "type": "main", + "index": 0 + } + ] + ] + }, + "PostGitea": { + "main": [ + [ + { + "node": "ForEach", + "type": "main", + "index": 0 + } + ] + ] + }, + "Schedule Trigger": { + "main": [ + [ + { + "node": "Globals", + "type": "main", + "index": 0 + } + ] + ] + }, + "SetDataCreateNode": { + "main": [ + [ + { + "node": "Base64EncodeCreate", + "type": "main", + "index": 0 + } + ] + ] + }, + "SetDataUpdateNode": { + "main": [ + [ + { + "node": "Base64EncodeUpdate", + "type": "main", + "index": 0 + } + ] + ] + }, + "Base64EncodeCreate": { + "main": [ + [ + { + "node": "PostGitea", + "type": "main", + "index": 0 + } + ] + ] + }, + "Base64EncodeUpdate": { + "main": [ + [ + { + "node": "Changed", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "settings": { + "executionOrder": "v1" + }, + "staticData": null, + "meta": null, + "pinData": {}, + "versionId": "49f3b1f9-d281-48df-82e3-26402fb9bd2a", + "triggerCount": 0, + "shared": [ + { + "updatedAt": "2025-11-23T08:34:14.534Z", + "createdAt": "2025-11-23T08:34:14.534Z", + "role": "workflow:owner", + "workflowId": "fsaojBpJsioS4FYe", + "projectId": "S4tmpzSj6JlGFOze" + } + ], + "tags": [] +} \ No newline at end of file