Automatisation de Sauvegarde de Données vers GitHub

Ce workflow n8n automatise la sauvegarde des données de vos instances vers un dépôt GitHub. Il permet de s'assurer que toutes les informations critiques sont sécurisées et accessibles en cas de besoin. En automatisant ce processus, les entreprises peuvent réduire le risque de perte de données tout en économisant du temps et des ressources humaines. Le workflow compare les versions actuelles et précédentes des fichiers pour déterminer s'il y a des différences, garantissant ainsi que seules les modifications nécessaires sont enregistrées.

86,416 vues
21,101 copies
Automatisation

Documentation Complète

📋 Automatisation de Sauvegarde de Données vers GitHub

💡 Description

Ce workflow n8n automatise la sauvegarde des données de vos instances vers un dépôt GitHub. Il permet de s'assurer que toutes les informations critiques sont sécurisées et accessibles en cas de besoin. En automatisant ce processus, les entreprises peuvent réduire le risque de perte de données tout en économisant du temps et des ressources humaines. Le workflow compare les versions actuelles et précédentes des fichiers pour déterminer s'il y a des différences, garantissant ainsi que seules les modifications nécessaires sont enregistrées.

📈 Impact & ROI: Réduction significative des risques liés à la perte de données et optimisation des opérations IT grâce à l'automatisation.

🚀 Fonctionnalités Clés

  • ✅ Automatisation complète du processus de sauvegarde
  • ✅ Comparaison intelligente des fichiers pour éviter les doublons
  • ✅ Gain de temps grâce à l'exécution planifiée
  • ✅ Intégration transparente avec GitHub

📊 Architecture Technique

24
Nodes
19
Connexions
5
Services

🔌 Services Intégrés

GitHubHTTP RequestCodeExecute CommandSchedule Trigger

🔧 Composition du Workflow

NodeTypeDescription
On clicking 'execute'manualTriggerTraitement des données
Sticky NotestickyNoteTraitement des données
ReturnsetTraitement des données
Get FilehttpRequestRequête HTTP vers une API externe
If file too largeifCondition logique pour router le flux
Merge ItemsmergeFusion de plusieurs branches de données
isDiffOrNewcodeTraitement des données
Check StatusswitchTraitement des données
Same file - Do nothingnoOpTraitement des données
File is differentnoOpTraitement des données
File is newnoOpTraitement des données
Create new filegithubTraitement des données
Edit existing filegithubTraitement des données
Loop Over ItemssplitInBatchesDivision des données en plusieurs branches
Schedule TriggerscheduleTriggerTraitement des données
Sticky Note1stickyNoteTraitement des données
Sticky Note2stickyNoteTraitement des données
Get file datagithubTraitement des données
GlobalssetTraitement des données
Execute CommandexecuteCommandTraitement des données
JSON formattingcodeTraitement des données
Sticky Note3stickyNoteTraitement des données
Execute WorkflowexecuteWorkflowTraitement des données
Execute Workflow TriggerexecuteWorkflowTriggerTraitement des données

📖 Guide d'Implémentation

  1. Import du workflow: Téléchargez le fichier JSON et importez-le dans votre instance n8n
  2. Configuration des credentials: Configurez les accès pour chaque service utilisé
  3. Personnalisation: Adaptez les paramètres selon vos besoins spécifiques
  4. Test: Exécutez le workflow en mode test pour vérifier le bon fonctionnement
  5. Activation: Activez le workflow pour une exécution automatique

🏷️ Tags

sauvegardeGitHubautomatisation

Structure JSON

Voir le code JSON complet
{
    "meta": {
        "instanceId": "d6b502dfa4d9dd072cdc5c2bb763558661053f651289291352a84403e01b3d1b",
        "templateCredsSetupCompleted": true
    },
    "nodes": [
        {
            "id": "4377c764-07f3-4304-8105-d3f009925917",
            "name": "On clicking 'execute'",
            "type": "n8n-nodes-base.manualTrigger",
            "position": [
                1780,
                520
            ],
            "parameters": [],
            "typeVersion": 1
        },
        {
            "id": "10f6ea70-c2cb-4463-972c-e2fdef3e837a",
            "name": "Sticky Note",
            "type": "n8n-nodes-base.stickyNote",
            "position": [
                1339.5461279763795,
                900
            ],
            "parameters": {
                "color": 6,
                "width": 2086.845881354743,
                "height": 750.8363163824032,
                "content": "## Subworkflow"
            },
            "typeVersion": 1
        },
        {
            "id": "d22236c2-578c-400b-b3e5-354498620c39",
            "name": "Return",
            "type": "n8n-nodes-base.set",
            "position": [
                3220,
                1100
            ],
            "parameters": {
                "options": [],
                "assignments": {
                    "assignments": [
                        {
                            "id": "8d513345-6484-431f-afb7-7cf045c90f4f",
                            "name": "Done",
                            "type": "boolean",
                            "value": true
                        }
                    ]
                }
            },
            "typeVersion": 3.3
        },
        {
            "id": "943eed85-d4cd-4ec5-b278-d143b0f6bd15",
            "name": "Get File",
            "type": "n8n-nodes-base.httpRequest",
            "position": [
                2320,
                980
            ],
            "parameters": {
                "url": "={{ $json.download_url }}",
                "options": []
            },
            "typeVersion": 4.2
        },
        {
            "id": "124ebdd7-c2c1-4fec-89d3-596f034e0fe7",
            "name": "If file too large",
            "type": "n8n-nodes-base.if",
            "position": [
                2120,
                1000
            ],
            "parameters": {
                "options": [],
                "conditions": {
                    "options": {
                        "version": 1,
                        "leftValue": "",
                        "caseSensitive": true,
                        "typeValidation": "strict"
                    },
                    "combinator": "and",
                    "conditions": [
                        {
                            "id": "45ce825e-9fa6-430c-8931-9aaf22c42585",
                            "operator": {
                                "type": "string",
                                "operation": "empty",
                                "singleValue": true
                            },
                            "leftValue": "={{ $json.content }}",
                            "rightValue": ""
                        },
                        {
                            "id": "9619a55f-7fb1-4f24-b1a7-7aeb82365806",
                            "operator": {
                                "type": "string",
                                "operation": "notExists",
                                "singleValue": true
                            },
                            "leftValue": "={{ $json.error }}",
                            "rightValue": ""
                        }
                    ]
                }
            },
            "typeVersion": 2
        },
        {
            "id": "751621b4-4f99-4178-a691-40fc7488874b",
            "name": "Merge Items",
            "type": "n8n-nodes-base.merge",
            "position": [
                2120,
                1260
            ],
            "parameters": [],
            "typeVersion": 2
        },
        {
            "id": "8892eb02-0e8e-4617-85e6-e6f188361f95",
            "name": "isDiffOrNew",
            "type": "n8n-nodes-base.code",
            "position": [
                2320,
                1260
            ],
            "parameters": {
                "jsCode": "const orderJsonKeys = (jsonObj) => {\n  const ordered = {};\n  Object.keys(jsonObj).sort().forEach(key => {\n    ordered[key] = jsonObj[key];\n  });\n  return ordered;\n}\n\n\/\/ Check if file returned with content\nif (Object.keys($input.all()[0].json).includes(\"content\")) {\n  \/\/ Decode base64 content and parse JSON\n  const origWorkflow = JSON.parse(Buffer.from($input.all()[0].json.content, 'base64').toString());\n  const n8nWorkflow = $input.all()[1].json;\n  \n  \/\/ Order JSON objects\n  const orderedOriginal = orderJsonKeys(origWorkflow);\n  const orderedActual = orderJsonKeys(n8nWorkflow);\n\n  \/\/ Determine difference\n  if (JSON.stringify(orderedOriginal) === JSON.stringify(orderedActual)) {\n    $input.all()[0].json.github_status = \"same\";\n  } else {\n    $input.all()[0].json.github_status = \"different\";\n    $input.all()[0].json.n8n_data_stringy = JSON.stringify(orderedActual, null, 2);\n  }\n  $input.all()[0].json.content_decoded = orderedOriginal;\n\/\/ No file returned \/ new workflow\n} else if (Object.keys($input.all()[0].json).includes(\"data\")) {\n  const origWorkflow = JSON.parse($input.all()[0].json.data);\n  const n8nWorkflow = $input.all()[1].json;\n  \n  \/\/ Order JSON objects\n  const orderedOriginal = orderJsonKeys(origWorkflow);\n  const orderedActual = orderJsonKeys(n8nWorkflow);\n\n  \/\/ Determine difference\n  if (JSON.stringify(orderedOriginal) === JSON.stringify(orderedActual)) {\n    $input.all()[0].json.github_status = \"same\";\n  } else {\n    $input.all()[0].json.github_status = \"different\";\n    $input.all()[0].json.n8n_data_stringy = JSON.stringify(orderedActual, null, 2);\n  }\n  $input.all()[0].json.content_decoded = orderedOriginal;\n\n} else {\n  \/\/ Order JSON object\n  const n8nWorkflow = $input.all()[1].json;\n  const orderedActual = orderJsonKeys(n8nWorkflow);\n  \n  \/\/ Proper formatting\n  $input.all()[0].json.github_status = \"new\";\n  $input.all()[0].json.n8n_data_stringy = JSON.stringify(orderedActual, null, 2);\n}\n\n\/\/ Return items\nreturn $input.all();"
            },
            "typeVersion": 1
        },
        {
            "id": "bfddb2a2-c149-4710-bd77-b368d641114d",
            "name": "Check Status",
            "type": "n8n-nodes-base.switch",
            "position": [
                2540,
                1260
            ],
            "parameters": {
                "rules": {
                    "rules": [
                        {
                            "value2": "same"
                        },
                        {
                            "output": 1,
                            "value2": "different"
                        },
                        {
                            "output": 2,
                            "value2": "new"
                        }
                    ]
                },
                "value1": "={{$json.github_status}}",
                "dataType": "string"
            },
            "typeVersion": 1
        },
        {
            "id": "681e54af-b916-416d-9801-ac38a5882bcf",
            "name": "Same file - Do nothing",
            "type": "n8n-nodes-base.noOp",
            "position": [
                2760,
                1100
            ],
            "parameters": [],
            "typeVersion": 1
        },
        {
            "id": "38b2041d-1d56-436f-aa04-79d7241dcc74",
            "name": "File is different",
            "type": "n8n-nodes-base.noOp",
            "position": [
                2760,
                1260
            ],
            "parameters": [],
            "typeVersion": 1
        },
        {
            "id": "ae33280d-10d5-4882-be9b-7972394357e1",
            "name": "File is new",
            "type": "n8n-nodes-base.noOp",
            "position": [
                2760,
                1420
            ],
            "parameters": [],
            "typeVersion": 1
        },
        {
            "id": "bea3995f-9f34-4119-a6cf-20281e70d685",
            "name": "Create new file",
            "type": "n8n-nodes-base.github",
            "position": [
                2980,
                1420
            ],
            "parameters": {
                "owner": {
                    "__rl": true,
                    "mode": "name",
                    "value": "={{ $('Globals').item.json.repo.owner }}"
                },
                "filePath": "={{ $('Globals').item.json.repo.path }}{{$('Execute Workflow Trigger').first().json.id}}.json",
                "resource": "file",
                "repository": {
                    "__rl": true,
                    "mode": "name",
                    "value": "={{ $('Globals').item.json.repo.name }}"
                },
                "fileContent": "={{$('isDiffOrNew').item.json[\"n8n_data_stringy\"]}}",
                "commitMessage": "={{$('Execute Workflow Trigger').first().json.name}} ({{$json.github_status}})"
            },
            "credentials": {
                "githubApi": {
                    "id": "3mfzXcMjoqNHsujs",
                    "name": "GitHub account"
                }
            },
            "typeVersion": 1
        },
        {
            "id": "d9172af3-55f8-4b99-b462-3e6e718b5a77",
            "name": "Edit existing file",
            "type": "n8n-nodes-base.github",
            "position": [
                2980,
                1240
            ],
            "parameters": {
                "owner": {
                    "__rl": true,
                    "mode": "name",
                    "value": "={{ $('Globals').item.json.repo.owner }}"
                },
                "filePath": "={{ $('Globals').item.json.repo.path }}{{$('Execute Workflow Trigger').first().json.id}}.json",
                "resource": "file",
                "operation": "edit",
                "repository": {
                    "__rl": true,
                    "mode": "name",
                    "value": "={{ $('Globals').item.json.repo.name }}"
                },
                "fileContent": "={{$('isDiffOrNew').item.json[\"n8n_data_stringy\"]}}",
                "commitMessage": "={{$('Execute Workflow Trigger').first().json.name}} ({{$json.github_status}})"
            },
            "credentials": {
                "githubApi": {
                    "id": "3mfzXcMjoqNHsujs",
                    "name": "GitHub account"
                }
            },
            "typeVersion": 1
        },
        {
            "id": "d9589e32-ed20-46e7-9427-1680c6222406",
            "name": "Loop Over Items",
            "type": "n8n-nodes-base.splitInBatches",
            "position": [
                2380,
                620
            ],
            "parameters": {
                "options": []
            },
            "typeVersion": 3
        },
        {
            "id": "e1530650-aa76-4ab3-b5bb-cd6b805ea656",
            "name": "Schedule Trigger",
            "type": "n8n-nodes-base.scheduleTrigger",
            "position": [
                1780,
                720
            ],
            "parameters": {
                "rule": {
                    "interval": [
                        {
                            "field": "hours",
                            "hoursInterval": 2
                        }
                    ]
                }
            },
            "typeVersion": 1.2
        },
        {
            "id": "79910589-f40f-46fa-a704-eaa65157a17a",
            "name": "Sticky Note1",
            "type": "n8n-nodes-base.stickyNote",
            "position": [
                1340,
                278.28654385738866
            ],
            "parameters": {
                "color": 4,
                "width": 365.19481715599653,
                "height": 596.4810912485963,
                "content": "## Backup to GitHub \nThis workflow will backup all instance credentials to GitHub.\n\nThe files are saved `ID.json` for the filename.\n\n### Setup\nOpen `Globals` node and update the values below 👇\n\n- **repo.owner:** your Github username\n- **repo.name:** the name of your repository\n- **repo.path:** the folder to use within the repository. If it doesn't exist it will be created.\n\n\nIf your username was `john-doe` and your repository was called `n8n-backups` and you wanted the credentials to go into a `credentials` folder you would set:\n\n- repo.owner - john-doe\n- repo.name - n8n-backups\n- repo.path - credentials\/\n\n\nThe workflow calls itself using a subworkflow, to help reduce memory usage."
            },
            "typeVersion": 1
        },
        {
            "id": "e16c9874-1a35-41c4-8410-0c42efe17770",
            "name": "Sticky Note2",
            "type": "n8n-nodes-base.stickyNote",
            "position": [
                1740,
                440
            ],
            "parameters": {
                "color": 7,
                "width": 1028.7522287279464,
                "height": 434.88564057365943,
                "content": "## Main workflow loop"
            },
            "typeVersion": 1
        },
        {
            "id": "a1464b91-516a-4fd9-9235-20de50e74cb2",
            "name": "Get file data",
            "type": "n8n-nodes-base.github",
            "position": [
                1920,
                1000
            ],
            "parameters": {
                "owner": {
                    "__rl": true,
                    "mode": "name",
                    "value": "={{ $json.repo.owner }}"
                },
                "filePath": "={{ $json.repo.path }}{{ $('Execute Workflow Trigger').item.json.id }}.json",
                "resource": "file",
                "operation": "get",
                "repository": {
                    "__rl": true,
                    "mode": "name",
                    "value": "={{ $json.repo.name }}"
                },
                "asBinaryProperty": false,
                "additionalParameters": []
            },
            "credentials": {
                "githubApi": {
                    "id": "3mfzXcMjoqNHsujs",
                    "name": "GitHub account"
                }
            },
            "typeVersion": 1,
            "continueOnFail": true,
            "alwaysOutputData": true
        },
        {
            "id": "eb2fe87f-f3af-4215-ac1f-7c2b45e8aff6",
            "name": "Globals",
            "type": "n8n-nodes-base.set",
            "position": [
                1720,
                1160
            ],
            "parameters": {
                "options": [],
                "assignments": {
                    "assignments": [
                        {
                            "id": "6cf546c5-5737-4dbd-851b-17d68e0a3780",
                            "name": "repo.owner",
                            "type": "string",
                            "value": "john-doe"
                        },
                        {
                            "id": "452efa28-2dc6-4ea3-a7a2-c35d100d0382",
                            "name": "repo.name",
                            "type": "string",
                            "value": "n8n-backup"
                        },
                        {
                            "id": "81c4dc54-86bf-4432-a23f-22c7ea831e74",
                            "name": "repo.path",
                            "type": "string",
                            "value": "credentials\/"
                        }
                    ]
                }
            },
            "typeVersion": 3.4
        },
        {
            "id": "f4498ab4-1760-4849-9fe1-ecfcd7baa9f3",
            "name": "Execute Command",
            "type": "n8n-nodes-base.executeCommand",
            "position": [
                2000,
                620
            ],
            "parameters": {
                "command": "npx n8n export:credentials --all --decrypted"
            },
            "typeVersion": 1
        },
        {
            "id": "d453a000-40ef-43f5-b108-5eb30422d1a3",
            "name": "JSON formatting",
            "type": "n8n-nodes-base.code",
            "position": [
                2180,
                620
            ],
            "parameters": {
                "jsCode": "\/\/ Function to beautify JSON\nfunction beautifyJson(jsonString) {\n  try {\n    \/\/ Parse the JSON string\n    const jsonObject = JSON.parse(jsonString);\n\n    \/\/ Format the JSON with indentation\n    return jsonObject; \/\/ Return the parsed object directly\n  } catch (error) {\n    \/\/ Return the error message if JSON is invalid\n    return `Invalid JSON: ${error.message}`;\n  }\n}\n\n\/\/ Retrieve the JSON object from the input data\nconst input = $input.all()[0].json;\n\n\/\/ Extract the JSON string from the stdout field\nconst jsonString = input.stdout.match(\/\\[{.*}\\]\/s);\n\n\/\/ Check if a valid JSON string is found\nif (!jsonString) {\n  return {\n    json: {\n      error: \"No valid JSON string found in stdout.\"\n    }\n  };\n}\n\n\/\/ Beautify the JSON\nconst beautifiedJson = beautifyJson(jsonString[0]);\n\n\/\/ Output the beautified JSON, ensuring each entry is in an object with a 'json' key\nconst output = beautifiedJson.map(entry => ({ json: entry }));\n\n\/\/ Return the output\nreturn output;\n"
            },
            "typeVersion": 2
        },
        {
            "id": "49dbf875-7345-4241-a7fc-f42e53aef64e",
            "name": "Sticky Note3",
            "type": "n8n-nodes-base.stickyNote",
            "position": [
                1680,
                1060
            ],
            "parameters": {
                "color": 4,
                "width": 150,
                "height": 80,
                "content": "## Edit this node 👇"
            },
            "typeVersion": 1
        },
        {
            "id": "98158f3e-7aca-456b-994c-4c795d31c18c",
            "name": "Execute Workflow",
            "type": "n8n-nodes-base.executeWorkflow",
            "position": [
                2600,
                620
            ],
            "parameters": {
                "mode": "each",
                "options": [],
                "workflowId": "={{ $workflow.id }}"
            },
            "typeVersion": 1
        },
        {
            "id": "d8c52eb7-bcb0-49e7-bb32-7499b1ca22cd",
            "name": "Execute Workflow Trigger",
            "type": "n8n-nodes-base.executeWorkflowTrigger",
            "position": [
                1440,
                1280
            ],
            "parameters": {
                "inputSource": "passthrough"
            },
            "typeVersion": 1.1
        }
    ],
    "pinData": [],
    "connections": {
        "Globals": {
            "main": [
                [
                    {
                        "node": "Get file data",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Get File": {
            "main": [
                [
                    {
                        "node": "Merge Items",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "File is new": {
            "main": [
                [
                    {
                        "node": "Create new file",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Merge Items": {
            "main": [
                [
                    {
                        "node": "isDiffOrNew",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "isDiffOrNew": {
            "main": [
                [
                    {
                        "node": "Check Status",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Check Status": {
            "main": [
                [
                    {
                        "node": "Same file - Do nothing",
                        "type": "main",
                        "index": 0
                    }
                ],
                [
                    {
                        "node": "File is different",
                        "type": "main",
                        "index": 0
                    }
                ],
                [
                    {
                        "node": "File is new",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Get file data": {
            "main": [
                [
                    {
                        "node": "If file too large",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Create new file": {
            "main": [
                [
                    {
                        "node": "Return",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Execute Command": {
            "main": [
                [
                    {
                        "node": "JSON formatting",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "JSON formatting": {
            "main": [
                [
                    {
                        "node": "Loop Over Items",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Loop Over Items": {
            "main": [
                [],
                [
                    {
                        "node": "Execute Workflow",
                        "type": "main",
                        "index": 0
                    },
                    {
                        "node": "Execute Workflow",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Execute Workflow": {
            "main": [
                [
                    {
                        "node": "Loop Over Items",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Schedule Trigger": {
            "main": [
                [
                    {
                        "node": "Execute Command",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "File is different": {
            "main": [
                [
                    {
                        "node": "Edit existing file",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "If file too large": {
            "main": [
                [
                    {
                        "node": "Get File",
                        "type": "main",
                        "index": 0
                    }
                ],
                [
                    {
                        "node": "Merge Items",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Edit existing file": {
            "main": [
                [
                    {
                        "node": "Return",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "On clicking 'execute'": {
            "main": [
                [
                    {
                        "node": "Execute Command",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Same file - Do nothing": {
            "main": [
                [
                    {
                        "node": "Return",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Execute Workflow Trigger": {
            "main": [
                [
                    {
                        "node": "Globals",
                        "type": "main",
                        "index": 0
                    },
                    {
                        "node": "Merge Items",
                        "type": "main",
                        "index": 1
                    }
                ]
            ]
        }
    }
}
                                

Workflows Similaires

Automatisez le Résumé de Vos Emails avec A.I. et Messagerie

Ce workflow n8n vous permet d'automatiser la gestion de vos emails en utilisant l'intelligence artificielle pour résume...

Automatisation de gestion des réunions Zoom et communication

Ce workflow est conçu pour automatiser le processus de planification et de gestion des réunions Zoom tout en assurant ...

Automatisez vos Tweets d'images humoristiques à 17h

Ce workflow n8n est conçu pour les professionnels des réseaux sociaux cherchant à automatiser leur contenu humoristiq...