diff --git a/scripts/addAppDbUser.sh b/scripts/addAppDbUser.sh new file mode 100644 index 0000000..782d40e --- /dev/null +++ b/scripts/addAppDbUser.sh @@ -0,0 +1,16 @@ +DB_USER=$1 +DB_PASSWORD=$2 +DB_HOST=$3 +ADMIN_PASSWORD=$(pwgen 10 1) +MYSQL=$(which mysql) +JEM=$(which jem) +cmd="CREATE USER '${DB_USER}'@'localhost' IDENTIFIED BY '${DB_PASSWORD}'; CREATE USER '${DB_USER}'@'%' IDENTIFIED BY '${DB_PASSWORD}'; GRANT ALL PRIVILEGES ON *.* TO '${DB_USER}'@'localhost' WITH GRANT OPTION; GRANT ALL PRIVILEGES ON *.* TO '${DB_USER}'@'%' WITH GRANT OPTION; FLUSH PRIVILEGES;" +unset resp; +resp=$($MYSQL -u${DB_USER} -p${DB_PASSWORD} -h ${DB_HOST} --execute="SHOW DATABASES;") +[ -z "$resp" ] && { + echo "Creating the DB user for application" + $JEM passwd set -p ${ADMIN_PASSWORD} + $MYSQL -uroot -p${ADMIN_PASSWORD} -h ${DB_HOST} --execute="$cmd" +} || { + echo "[Info] User ${DB_USER} already exists." +} \ No newline at end of file diff --git a/scripts/backup-addon b/scripts/backup-addon new file mode 100644 index 0000000..5166e0f --- /dev/null +++ b/scripts/backup-addon @@ -0,0 +1,8 @@ +/var/log/backup_addon.log { + weekly + rotate 52 + missingok + notifempty + compress + copytruncate +} \ No newline at end of file diff --git a/scripts/backup-main.js b/scripts/backup-main.js new file mode 100644 index 0000000..32633a4 --- /dev/null +++ b/scripts/backup-main.js @@ -0,0 +1,44 @@ +//@auth + +var action = getParam("action", "backup"), + baseUrl = "${baseUrl}"; + +function run() { + var BackupManager = use("scripts/mb-backup-manager.js", { + session : session, + baseUrl : baseUrl, + uid : user.uid, + cronTime : "${cronTime}", + scriptName : "${scriptName}", + envName : "${envName}", + envAppid : "${envAppid}", + storageNodeId : "${storageNodeId}", + backupExecNode : "${backupExecNode}", + backupCount : "${backupCount}", + storageEnv : "${storageEnv}" + }); + + api.local.ReturnResult( + BackupManager.invoke(action) + ); +} + +function use(script, config) { + var Transport = com.hivext.api.core.utils.Transport, + body = new Transport().get(baseUrl + "/" + script + "?_r=" + Math.random()); + var debug = baseUrl + "/" + script + "?_r=" + Math.random(); + + return new (new Function("return " + body)())(config); +} + +try { + run(); +} catch (ex) { + var resp = { + result : com.hivext.api.Response.ERROR_UNKNOWN, + error: "Error: " + toJSON(ex) + }; + + api.marketplace.console.WriteLog(appid, signature, "ERROR: " + resp); + api.local.ReturnResult(resp); +} \ No newline at end of file diff --git a/scripts/backupOnBeforeInit.js b/scripts/backupOnBeforeInit.js new file mode 100644 index 0000000..217bd9d --- /dev/null +++ b/scripts/backupOnBeforeInit.js @@ -0,0 +1,53 @@ +var resp = api.env.control.GetEnvs(); +if (resp.result !== 0) return resp; +var envs = []; +var nodes = {}; +for (var i = 0, envInfo, env; envInfo = resp.infos[i]; i++) { + if (envInfo.envGroups.includes("WP Backup") || envInfo.envGroups.includes("Backup storage nodes")) { + env = envInfo.env + if (env.status == 1) { + for (var j = 0, node; node = envInfo.nodes[j]; j++) { + nodes[env.envName] = nodes[env.envName] || []; + nodes[env.envName].groups = nodes[env.envName].groups || {}; + if (!nodes[env.envName].groups[node.nodeGroup]) nodes[env.envName].push({ + value: node.nodeGroup, + caption: (node.displayName || node.name) + ' (' + node.nodeGroup + ')' + }); + nodes[env.envName].groups[node.nodeGroup] = true; + } + if (nodes[env.envName] && nodes[env.envName].length > 0) { + envs.push({ + value: env.envName, + caption: (env.displayName + " (" + env.envName + ")" || env.envName) + }); + } + } + } +} + +if (envs.length > 0) { + jps.settings.main.fields[1].values = envs; + jps.settings.main.fields[1].default = envs[0].value; +} + +import java.util.TimeZone; +var zones = toNative(TimeZone.getAvailableIDs()); +var values = {}; + +for (var i = 0, n = zones.length; i < n; i++) { + var offset = TimeZone.getTimeZone(zones[i]).getRawOffset()/3600000; + var m = offset % 1; + if (m != 0) m = Math.abs(m * 60); + if (m < 10) m = "0" + m; + var h = Math.floor(offset); + if (Math.abs(h) < 10) h = h < 0 ? "-0" + Math.abs(h) : "+0" + h; else if (h >= 0) h = "+" + h; + values[zones[i]] = zones[i] + (zones[i] == "GMT" ? "" : " (GMT" + h + ":" + m + ")"); +} + +jps.settings.main.fields[0].showIf[2][2].values = values; +jps.settings.main.fields[0].showIf[2][2].value = "GMT0"; + +return { + result: 0, + settings: jps.settings +}; \ No newline at end of file diff --git a/scripts/check_app.sh b/scripts/check_app.sh new file mode 100644 index 0000000..303ed55 --- /dev/null +++ b/scripts/check_app.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +if [ -e /var/www/webroot/ROOT/wp-config.php ]; then + echo "$(date) trying to install the backup add-on" >> /var/log/backup_addon.log + if [ -e /home/jelastic/bin/wp ]; then + /home/jelastic/bin/wp --info >> /var/log/backup_addon.log + fi +else + echo "$(date) The application deployed to WEBROOT cannot be backuped by Jelastic backup add-on" >> /var/log/backup_addon.log + echo "Non-supported" +fi \ No newline at end of file diff --git a/scripts/check_app.sh.sh b/scripts/check_app.sh.sh new file mode 100644 index 0000000..303ed55 --- /dev/null +++ b/scripts/check_app.sh.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +if [ -e /var/www/webroot/ROOT/wp-config.php ]; then + echo "$(date) trying to install the backup add-on" >> /var/log/backup_addon.log + if [ -e /home/jelastic/bin/wp ]; then + /home/jelastic/bin/wp --info >> /var/log/backup_addon.log + fi +else + echo "$(date) The application deployed to WEBROOT cannot be backuped by Jelastic backup add-on" >> /var/log/backup_addon.log + echo "Non-supported" +fi \ No newline at end of file diff --git a/scripts/configOnBeforeInit.js b/scripts/configOnBeforeInit.js new file mode 100644 index 0000000..fdcddc2 --- /dev/null +++ b/scripts/configOnBeforeInit.js @@ -0,0 +1,138 @@ +var storage_unavailable_markup = ""; +var resp = api.env.control.GetEnvs(); +if (resp.result !== 0) return resp; +var envs = []; +var nodes = {}; +var currentStorageExists = false; +var scheduleType = '${settings.scheduleType}'; +for (var i = 0, envInfo, env; envInfo = resp.infos[i]; i++) { + if (envInfo.envGroups.includes("WP Backup") || envInfo.envGroups.includes("Backup storage nodes")) { + env = envInfo.env + if (env.status == 1) { + for (var j = 0, node; node = envInfo.nodes[j]; j++) { + nodes[env.envName] = nodes[env.envName] || []; + nodes[env.envName].groups = nodes[env.envName].groups || {}; + if (!nodes[env.envName].groups[node.nodeGroup]) nodes[env.envName].push({ + value: node.nodeGroup, + caption: (node.displayName || node.name) + ' (' + node.nodeGroup + ')' + }); + nodes[env.envName].groups[node.nodeGroup] = true; + if ( env.envName == '${settings.storageName}' ) { + currentStorageExists = true; + } + } + if (nodes[env.envName] && nodes[env.envName].length > 0) { + envs.push({ + value: env.envName, + caption: (env.displayName + " (" + env.envName + ")" || env.envName) + }); + } + } + } +} + +jps.settings.main.fields[1].values = envs; +jps.settings.main.fields[1].default = ""; +if (envs.length > 0) { + if (currentStorageExists == true) { + jps.settings.main.fields[1].default = '${settings.storageName}'; + } else { + jps.settings.main.fields[1].default = envs[0].value; + } +} else { + storage_unavailable_markup = "There are no available backup storages on current account." +} + +import java.util.TimeZone; +var zones = toNative(TimeZone.getAvailableIDs()); +var values = {}; + +for (var i = 0, n = zones.length; i < n; i++) { + var offset = TimeZone.getTimeZone(zones[i]).getRawOffset()/3600000; + var m = offset % 1; + if (m != 0) m = Math.abs(m * 60); + if (m < 10) m = "0" + m; + var h = Math.floor(offset); + if (Math.abs(h) < 10) h = h < 0 ? "-0" + Math.abs(h) : "+0" + h; else if (h >= 0) h = "+" + h; + values[zones[i]] = zones[i] + (zones[i] == "GMT" ? "" : " (GMT" + h + ":" + m + ")"); +} + +jps.settings.main.fields[0].default = '${settings.scheduleType}'; + +if (scheduleType == '1') { + jps.settings.main.fields[0].showIf[1][0].default = '${settings.cronTime}'; +} else if (scheduleType == '2') { + jps.settings.main.fields[0].showIf[2][0].default = '${settings.backupTime}'; + var sun = ('${settings.sun}' === 'true'), + mon = ('${settings.mon}' === 'true'), + tue = ('${settings.tue}' === 'true'), + wed = ('${settings.wed}' === 'true'), + thu = ('${settings.thu}' === 'true'), + fri = ('${settings.fri}' === 'true'), + sat = ('${settings.sat}' === 'true'); + var selectedDays = { + "caption": "Days", + "type": "compositefield", + "name": "days", + "defaultMargins": "0 12 0 0", + "items": [ + { + "name": "sun", + "value": sun, + "type": "checkbox", + "caption": "Su" + }, + { + "name": "mon", + "value": mon, + "type": "checkbox", + "caption": "Mo" + }, + { + "name": "tue", + "value": tue, + "type": "checkbox", + "caption": "Tu" + }, + { + "name": "wed", + "value": wed, + "type": "checkbox", + "caption": "We" + }, + { + "name": "thu", + "value": thu, + "type": "checkbox", + "caption": "Th" + }, + { + "name": "fri", + "value": fri, + "type": "checkbox", + "caption": "Fr" + }, + { + "name": "sat", + "value": sat, + "type": "checkbox", + "caption": "Sa" + } + ] + }; + jps.settings.main.fields[0].showIf[2][1] = selectedDays; + jps.settings.main.fields[0].showIf[2][2].values = values; + jps.settings.main.fields[0].showIf[2][2].value = '${settings.tz}'; +} else { + jps.settings.main.fields[0].showIf[3][0].default = '${settings.cronTime}'; +} + +jps.settings.main.fields[2].default = '${settings.backupCount}'; + +if (storage_unavailable_markup.length > 0) { + jps.settings.main.fields.push( + {"type": "displayfield", "cls": "warning", "height": 30, "hideLabel": true, "markup": storage_unavailable_markup} + ) +} + +return settings; \ No newline at end of file diff --git a/scripts/getBackups.sh b/scripts/getBackups.sh new file mode 100644 index 0000000..a523a95 --- /dev/null +++ b/scripts/getBackups.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +if [ $# -eq 0 ]; then + ENV_LIST=$(ls -Qm /data); + OUTPUT_JSON="{\"result\": 0, \"envs\": [${ENV_LIST}" +else + ENV_NAME=$1 + [ -d "/data/$1" ] && BACKUP_LIST=$(RESTIC_PASSWORD="$1" restic -r /data/$1 snapshots|awk '{print $5}'|grep -o [0-9_-]*|awk '{print "\""$1"\""}'|tr '\n' ',') + OUTPUT_JSON="{\"result\": 0, \"backups\": [${BACKUP_LIST}" + [ -n "${BACKUP_LIST}" ] && OUTPUT_JSON=${OUTPUT_JSON::-1} +fi + +echo $OUTPUT_JSON]} \ No newline at end of file diff --git a/scripts/getStorageNode b/scripts/getStorageNode new file mode 100644 index 0000000..ef5be0d --- /dev/null +++ b/scripts/getStorageNode @@ -0,0 +1,10 @@ +var storageEnv = '${settings.storageName}' +var storageEnvShortName = storageEnv.split(".")[0] +var resp = api.environment.control.GetEnvInfo(storageEnvShortName, session) +if (resp.result != 0) return resp +for (var i = 0; resp.nodes; i++) { + var node = resp.nodes[i] + if (node.nodeGroup == 'storage' && node.ismaster) { + return { result: 0, storageCtid : node.id, storageEnvShortName : storageEnvShortName}; + } +} \ No newline at end of file diff --git a/scripts/getStorageNode.js b/scripts/getStorageNode.js new file mode 100644 index 0000000..ef5be0d --- /dev/null +++ b/scripts/getStorageNode.js @@ -0,0 +1,10 @@ +var storageEnv = '${settings.storageName}' +var storageEnvShortName = storageEnv.split(".")[0] +var resp = api.environment.control.GetEnvInfo(storageEnvShortName, session) +if (resp.result != 0) return resp +for (var i = 0; resp.nodes; i++) { + var node = resp.nodes[i] + if (node.nodeGroup == 'storage' && node.ismaster) { + return { result: 0, storageCtid : node.id, storageEnvShortName : storageEnvShortName}; + } +} \ No newline at end of file diff --git a/scripts/mb-backbackup-manager.js b/scripts/mb-backbackup-manager.js index e38e152..d6b3e63 100644 --- a/scripts/mb-backbackup-manager.js +++ b/scripts/mb-backbackup-manager.js @@ -37,7 +37,7 @@ function BackupManager(config) { }; me.backupMediaFiles = function() { - var backupName = me.getBackupName(BACKUP_TYPES.MEDIA); + var backupName = me.getBackupName(BACKUP_TYPES.ME DIA); // Placeholder for media files backup logic Logger.info("Media files backup completed: " + backupName); }; @@ -87,7 +87,3 @@ function BackupManager(config) { return config.envName + "_" + type + "_" + dateStr; }; } - -// Note: This code assumes the existence of several placeholders where actual backup and restore logic should be implemented. -// Depending on the storage and execution environment, this could involve executing shell commands, interacting with APIs, -// or using tools like Restic for actual data handling. diff --git a/scripts/multipleRestoreOnBeforeInit.js b/scripts/multipleRestoreOnBeforeInit.js new file mode 100644 index 0000000..c19da53 --- /dev/null +++ b/scripts/multipleRestoreOnBeforeInit.js @@ -0,0 +1,90 @@ +import org.json.JSONObject; +var Response = com.hivext.api.Response; +var storage_unavailable_markup = ""; +var storageInfo = getStorageNodeid(); +var storageEnvDomain = storageInfo.storageEnvShortName; +var storageEnvMasterId = storageInfo.storageCtid; + +resp = api.env.control.GetEnvInfo(storageEnvDomain, session); +if (resp.result != 0 && resp.result != 11) return resp; +if (resp.result == 11) { + storage_unavailable_markup = "Storage environment " + "${settings.storageName}" + " is deleted."; +} else if (resp.env.status == 1) { + var respUpdate = api.env.control.ExecCmdById(storageEnvDomain, session, storageEnvMasterId, toJSON([{"command": "/usr/bin/restic self-update 2>&1", "params": ""}]), false); + if (respUpdate.result != 0) return resp; + var backups = api.env.control.ExecCmdById(storageEnvDomain, session, storageEnvMasterId, toJSON([{"command": "/root/getBackupsAllEnvs.sh", "params": ""}]), false); + if (backups.result != 0) return resp; + var backupList = toNative(new JSONObject(String(backups.responses[0].out))); + var envs = prepareEnvs(backupList.envs); + var backups = prepareBackups(backupList.backups); +} else { + storage_unavailable_markup = "Storage environment " + storageEnvDomain + " is unavailable (stopped/sleeping)."; +} + +function getStorageNodeid(){ + let storageEnv = '${settings.storageName}'; + var storageEnvShortName = storageEnv.split(".")[0]; + let resp = api.environment.control.GetEnvInfo({ envName: storageEnvShortName }); + if (resp.result != 0) return resp; + + let storageNode = resp.nodes.filter(node => (node.nodeGroup == 'storage' && node.ismaster))[0]; + if (!storageNode) return { result: Response.OBJECT_NOT_EXIST, error: "storage node not found" }; + + return { result: 0, storageCtid : storageNode.id, storageEnvShortName: storageEnvShortName }; +} + +function prepareEnvs(values) { + var aResultValues = []; + + values = values || []; + + for (var i = 0, n = values.length; i < n; i++) { + aResultValues.push({ caption: values[i], value: values[i] }); + } + + return aResultValues; +} + +function prepareBackups(backups) { + var oResultBackups = {}; + var aValues; + + for (var envName in backups) { + if (Object.prototype.hasOwnProperty.call(backups, envName)) { + aValues = []; + + for (var i = 0, n = backups[envName].length; i < n; i++) { + aValues.push({ caption: backups[envName][i], value: backups[envName][i] }); + } + + oResultBackups[envName] = aValues; + } + } + + return oResultBackups; +} + +if (storage_unavailable_markup === "") { + settings.fields.push({ + "caption": "Restore from", + "type": "list", + "name": "backupedEnvName", + "required": true, + "values": envs + }, { + "caption": "Backup", + "type": "list", + "name": "backupDir", + "required": true, + "tooltip": "Select the time stamp for which you want to restore the DB dump", + "dependsOn": { + "backupedEnvName" : backups + } + }) +} else { + settings.fields.push( + {"type": "displayfield", "cls": "warning", "height": 30, "hideLabel": true, "markup": storage_unavailable_markup} + ) +} + +return settings; \ No newline at end of file diff --git a/scripts/restoreOnBeforeInit.js b/scripts/restoreOnBeforeInit.js new file mode 100644 index 0000000..35c8985 --- /dev/null +++ b/scripts/restoreOnBeforeInit.js @@ -0,0 +1,62 @@ +import org.json.JSONObject; +var Response = com.hivext.api.Response; +var storage_unavailable_markup = ""; +var storageInfo = getStorageNodeid(); +var storageEnvDomain = storageInfo.storageEnvShortName; +var storageEnvMasterId = storageInfo.storageCtid; +var backupedEnvDomain = '${env.envName}'; + +resp = api.env.control.GetEnvInfo(storageEnvDomain, session); +if (resp.result != 0 && resp.result != 11) return resp; +if (resp.result == 11) { + storage_unavailable_markup = "Storage environment " + "${settings.storageName}" + " is deleted."; +} else if (resp.env.status == 1) { + var respUpdate = api.env.control.ExecCmdById(storageEnvDomain, session, storageEnvMasterId, toJSON([{"command": "/usr/bin/restic self-update 2>&1", "params": ""}]), false); + if (respUpdate.result != 0) return resp; + var backups = api.env.control.ExecCmdById(storageEnvDomain, session, storageEnvMasterId, toJSON([{"command": "/root/getBackups.sh", "params": backupedEnvDomain}]), false).responses[0].out; + var backupList = toNative(new JSONObject(String(backups))).backups; + var backupListPrepared = prepareBackups(backupList); +} else { + storage_unavailable_markup = "Storage environment " + storageEnvDomain + " is unavailable (stopped/sleeping)."; +} + +function getStorageNodeid(){ + let storageEnv = '${settings.storageName}' + var storageEnvShortName = storageEnv.split(".")[0] + let resp = api.environment.control.GetEnvInfo({ envName: storageEnvShortName }) + if (resp.result != 0) return resp + + let storageNode = resp.nodes.filter(node => (node.nodeGroup == 'storage' && node.ismaster))[0]; + if (!storageNode) return { result: Response.OBJECT_NOT_EXIST, error: "storage node not found" }; + + return { result: 0, storageCtid : storageNode.id, storageEnvShortName: storageEnvShortName }; +} + +function prepareBackups(values) { + var aResultValues = []; + values = values || []; + for (var i = 0, n = values.length; i < n; i++) { + aResultValues.push({ + caption: values[i], + value: values[i] + }); + } + return aResultValues; +} + +if (storage_unavailable_markup === "") { + settings.fields.push({ + "caption": "Backup", + "type": "list", + "tooltip": "Select the time stamp for which you want to restore the contents of the web site", + "name": "backupDir", + "required": true, + "values": backupListPrepared + }) +} else { + settings.fields.push( + {"type": "displayfield", "cls": "warning", "height": 30, "hideLabel": true, "markup": storage_unavailable_markup} + ) +} + +return settings; \ No newline at end of file