268 lines
9.7 KiB
JavaScript
268 lines
9.7 KiB
JavaScript
'use strict';
|
|
|
|
const {
|
|
HASH_N_ROW
|
|
} = require("../_constants")["backup"];
|
|
|
|
var DB; //Container for database
|
|
|
|
//Backup Transfer
|
|
function sendBackupData(timestamp, checksum, ws) {
|
|
if (!timestamp) timestamp = 0;
|
|
else if (typeof timestamp === "string" && /\.\d{3}Z$/.test(timestamp))
|
|
timestamp = timestamp.substring(0, timestamp.length - 1);
|
|
let promises = [
|
|
backupSync_data(timestamp, ws),
|
|
backupSync_delete(timestamp, ws)
|
|
];
|
|
if (checksum)
|
|
promises.push(backupSync_checksum(ws));
|
|
Promise.allSettled(promises).then(result => {
|
|
let failedSync = [];
|
|
result.forEach(r => r.status === "rejected" ? failedSync.push(r.reason) : null);
|
|
if (failedSync.length) {
|
|
console.info("Backup Sync Failed:", failedSync);
|
|
ws.send(JSON.stringify({
|
|
command: "SYNC_END",
|
|
status: false,
|
|
info: failedSync
|
|
}));
|
|
} else {
|
|
console.info("Backup Sync completed");
|
|
ws.send(JSON.stringify({
|
|
command: "SYNC_END",
|
|
status: true
|
|
}));
|
|
}
|
|
});
|
|
}
|
|
|
|
function backupSync_delete(timestamp, ws) {
|
|
return new Promise((resolve, reject) => {
|
|
DB.query("SELECT * FROM _backup WHERE mode is NULL AND timestamp > ?", [timestamp]).then(result => {
|
|
ws.send(JSON.stringify({
|
|
command: "SYNC_DELETE",
|
|
delete_data: result
|
|
}));
|
|
resolve("deleteSync");
|
|
}).catch(error => {
|
|
console.error(error);
|
|
reject("deleteSync");
|
|
});
|
|
})
|
|
}
|
|
|
|
function backupSync_data(timestamp, ws) {
|
|
const sendTable = (table, id_list) => new Promise((res, rej) => {
|
|
DB.query(`SELECT * FROM ${table} WHERE id IN (${id_list})`)
|
|
.then(data => {
|
|
ws.send(JSON.stringify({
|
|
table,
|
|
command: "SYNC_UPDATE",
|
|
data
|
|
}));
|
|
res(table);
|
|
}).catch(error => {
|
|
console.error(error);
|
|
rej(table);
|
|
});
|
|
});
|
|
return new Promise((resolve, reject) => {
|
|
DB.query("SELECT * FROM _backup WHERE mode=TRUE AND timestamp > ?", [timestamp]).then(result => {
|
|
let sync_needed = {};
|
|
result.forEach(r => r.t_name in sync_needed ? sync_needed[r.t_name].push(r.id) : sync_needed[r.t_name] = [r.id]);
|
|
ws.send(JSON.stringify({
|
|
command: "SYNC_HEADER",
|
|
add_data: result
|
|
}));
|
|
let promises = [];
|
|
for (let table in sync_needed)
|
|
promises.push(sendTable(table, sync_needed[table]));
|
|
Promise.allSettled(promises).then(result => {
|
|
let failedTables = [];
|
|
result.forEach(r => r.status === "rejected" ? failedTables.push(r.reason) : null);
|
|
if (failedTables.length)
|
|
reject(["dataSync", failedTables]);
|
|
else
|
|
resolve("dataSync");
|
|
});
|
|
}).catch(error => {
|
|
console.error(error);
|
|
reject("dataSync");
|
|
});
|
|
});
|
|
}
|
|
|
|
function backupSync_checksum(ws) {
|
|
return new Promise((resolve, reject) => {
|
|
DB.query("SELECT DISTINCT t_name FROM _backup").then(result => {
|
|
let tableList = result.map(r => r['t_name']);
|
|
DB.query("CHECKSUM TABLE " + tableList.join()).then(result => {
|
|
let checksum = Object.fromEntries(result.map(r => [r.Table.split(".").pop(), r.Checksum]));
|
|
ws.send(JSON.stringify({
|
|
command: "SYNC_CHECKSUM",
|
|
checksum: checksum
|
|
}));
|
|
resolve("checksum");
|
|
}).catch(error => {
|
|
console.error(error);
|
|
reject("checksum");
|
|
})
|
|
}).catch(error => {
|
|
console.error(error);
|
|
reject("checksum");
|
|
})
|
|
})
|
|
|
|
}
|
|
|
|
function sendTableHash(tables, ws) {
|
|
const getHash = table => new Promise((res, rej) => {
|
|
DB.query("SHOW COLUMNS FROM " + table).then(result => {
|
|
let columns = result.map(r => r["Field"]).sort();
|
|
DB.query(`SELECT CEIL(id/${HASH_N_ROW}) as group_id, MD5(GROUP_CONCAT(${columns.map(c => `IFNULL(${c}, "NULL")`).join()})) as hash FROM ${table} GROUP BY group_id ORDER BY group_id`)
|
|
.then(result => res(Object.fromEntries(result.map(r => [r.group_id, r.hash]))))
|
|
.catch(error => rej(error))
|
|
}).catch(error => rej(error))
|
|
});
|
|
Promise.allSettled(tables.map(t => getHash(t))).then(result => {
|
|
let hashes = {};
|
|
for (let i in tables)
|
|
if (result[i].status === "fulfilled")
|
|
hashes[tables[i]] = result[i].value;
|
|
else
|
|
console.error(result[i].reason);
|
|
ws.send(JSON.stringify({
|
|
command: "SYNC_HASH",
|
|
hashes: hashes
|
|
}));
|
|
})
|
|
}
|
|
|
|
function sendTableData(tables, ws) {
|
|
let promises = [
|
|
tableSync_data(tables, ws),
|
|
tableSync_delete(tables, ws),
|
|
tableSync_checksum(Object.keys(tables), ws)
|
|
];
|
|
Promise.allSettled(promises).then(result => {
|
|
let failedSync = [];
|
|
result.forEach(r => r.status === "rejected" ? failedSync.push(r.reason) : null);
|
|
if (failedSync.length) {
|
|
console.info("Backup Sync Failed:", failedSync);
|
|
ws.send(JSON.stringify({
|
|
command: "SYNC_END",
|
|
status: false,
|
|
info: failedSync
|
|
}));
|
|
} else {
|
|
console.info("Backup Sync completed");
|
|
ws.send(JSON.stringify({
|
|
command: "SYNC_END",
|
|
status: true
|
|
}));
|
|
}
|
|
});
|
|
}
|
|
|
|
function tableSync_delete(tables, ws) {
|
|
let getDelete = (table, group_id) => new Promise((res, rej) => {
|
|
let id_end = group_id * HASH_N_ROW,
|
|
id_start = id_end - HASH_N_ROW + 1;
|
|
DB.query("SELECT * FROM _backup WHERE t_name=? AND mode is NULL AND (id BETWEEN ? AND ?)", [table, id_start, id_end])
|
|
.then(result => res(result))
|
|
.catch(error => rej(error))
|
|
})
|
|
return new Promise((resolve, reject) => {
|
|
let promises = [];
|
|
for (let t in tables)
|
|
for (let g_id in tables[t][1]) //tables[t] is [convertIntArray(hash_ref), convertIntArray(hash_cur)]
|
|
promises.push(getDelete(t, g_id));
|
|
Promise.allSettled(promises).then(results => {
|
|
let delete_sync = results.filter(r => r.status === "fulfilled").map(r => r.value); //Filtered results
|
|
delete_sync = [].concat(...delete_sync); //Convert 2d array into 1d
|
|
ws.send(JSON.stringify({
|
|
command: "SYNC_DELETE",
|
|
delete_data: delete_sync
|
|
}));
|
|
resolve("deleteSync");
|
|
});
|
|
})
|
|
}
|
|
|
|
function tableSync_data(tables, ws) {
|
|
const sendTable = (table, group_id) => new Promise((res, rej) => {
|
|
let id_end = group_id * HASH_N_ROW,
|
|
id_start = id_end - HASH_N_ROW + 1;
|
|
DB.query(`SELECT * FROM ${table} WHERE id BETWEEN ? AND ?`, [id_start, id_end]).then(data => {
|
|
ws.send(JSON.stringify({
|
|
table,
|
|
command: "SYNC_UPDATE",
|
|
data
|
|
}));
|
|
res(table);
|
|
}).catch(error => {
|
|
console.error(error);
|
|
rej(table);
|
|
});
|
|
});
|
|
const getUpdate = (table, group_id) => new Promise((res, rej) => {
|
|
let id_end = group_id * HASH_N_ROW,
|
|
id_start = id_end - HASH_N_ROW + 1;
|
|
DB.query("SELECT * FROM _backup WHERE t_name=? AND mode=TRUE AND (id BETWEEN ? AND ?)", [table, id_start, id_end])
|
|
.then(result => res(result))
|
|
.catch(error => rej(error))
|
|
})
|
|
return new Promise((resolve, reject) => {
|
|
let promises = [];
|
|
for (let t in tables)
|
|
for (let g_id of tables[t][0]) //tables[t] is [convertIntArray(hash_ref), convertIntArray(hash_cur)]
|
|
promises.push(getUpdate(t, g_id));
|
|
Promise.allSettled(promises).then(results => {
|
|
let update_sync = results.filter(r => r.status === "fulfilled").map(r => r.value); //Filtered results
|
|
update_sync = [].concat(...update_sync); //Convert 2d array into 1d
|
|
ws.send(JSON.stringify({
|
|
command: "SYNC_HEADER",
|
|
add_data: update_sync
|
|
}));
|
|
let promises = [];
|
|
for (let t in tables)
|
|
for (let g_id of tables[t][0]) //tables[t] is [convertIntArray(hash_ref), convertIntArray(hash_cur)]
|
|
promises.push(sendTable(t, g_id));
|
|
Promise.allSettled(promises).then(result => {
|
|
let failedTables = [];
|
|
result.forEach(r => r.status === "rejected" ? failedTables.push(r.reason) : null);
|
|
if (failedTables.length)
|
|
reject(["dataSync", [...new Set(failedTables)]]);
|
|
else
|
|
resolve("dataSync");
|
|
});
|
|
});
|
|
})
|
|
}
|
|
|
|
function tableSync_checksum(tables, ws) {
|
|
return new Promise((resolve, reject) => {
|
|
DB.query("CHECKSUM TABLE " + tables.join()).then(result => {
|
|
let checksum = Object.fromEntries(result.map(r => [r.Table.split(".").pop(), r.Checksum]));
|
|
ws.send(JSON.stringify({
|
|
command: "SYNC_CHECKSUM",
|
|
checksum: checksum
|
|
}));
|
|
resolve("checksum");
|
|
}).catch(error => {
|
|
console.error(error);
|
|
reject("checksum");
|
|
})
|
|
})
|
|
|
|
}
|
|
|
|
module.exports = {
|
|
sendBackupData,
|
|
sendTableHash,
|
|
sendTableData,
|
|
set DB(db) {
|
|
DB = db;
|
|
}
|
|
} |