SuperNodeStorage/src/client.js
sairajzero 43a3b16bbe Update database files and improvements
- moved base table struct to base_table.json
- moved cloud data struct to data_structure.json
- improvements to database module
- use database.DB from module.export cache instead of global mapping
2022-12-17 01:10:34 +05:30

277 lines
11 KiB
JavaScript

const { DB } = require("./database");
const { _list } = require("./intra");
global.INVALID = function (message) {
if (!(this instanceof INVALID))
return new INVALID(message);
this.message = message;
}
function processIncomingData(data) {
return new Promise((resolve, reject) => {
try {
data = JSON.parse(data);
} catch (error) {
return reject(INVALID("Data not in JSON-Format"));
};
let curTime = Date.now();
if (!data.time || data.time > curTime + floGlobals.sn_config.delayDelta ||
data.time < curTime - floGlobals.sn_config.delayDelta)
return reject(INVALID("Invalid Time"));
else {
let process;
if ('request' in data) //Request
process = processRequestFromUser(data.request);
else if ('message' in data) //Store data
process = processDataFromUser(data);
else if ('tag' in data) //Tag data
process = processTagFromUser(data);
else if ('note' in data)
process = processNoteFromUser(data);
/*
else if (data.edit)
return processEditFromUser(gid, uid, data);
else if (data.delete)
return processDeleteFromUser(gid, uid, data);
*/
else
return reject(INVALID("Invalid Data-format"));
process.then(result => {
console.debug(result);
resolve(result);
}).catch(error => {
(error instanceof INVALID ? console.debug : console.error)(error);
reject(error);
});
};
});
};
function processDataFromUser(data) {
return new Promise((resolve, reject) => {
if (!floCrypto.validateAddr(data.receiverID))
return reject(INVALID("Invalid receiverID"));
let closeNode = cloud.closestNode(data.receiverID);
if (!_list.serving.includes(closeNode))
return reject(INVALID("Incorrect Supernode"));
if (!floCrypto.validateAddr(data.senderID))
return reject(INVALID("Invalid senderID"));
if (!floCrypto.verifyPubKey(data.pubKey, data.senderID))
return reject(INVALID("Invalid pubKey"));
let hashcontent = ["receiverID", "time", "application", "type", "message", "comment"]
.map(d => data[d]).join("|");
if (!floCrypto.verifySign(hashcontent, data.sign, data.pubKey))
return reject(INVALID("Invalid signature"));
DB.addData(closeNode, {
vectorClock: `${Date.now()}_${data.senderID}`,
senderID: data.senderID,
receiverID: data.receiverID,
time: data.time,
application: data.application,
type: data.type,
message: data.message,
comment: data.comment,
sign: data.sign,
pubKey: data.pubKey
}).then(rb => {
DB.getData(closeNode, rb.vectorClock)
.then(result => resolve([result[0], 'DATA', rb]))
.catch(error => reject(error))
}).catch(error => reject(error));
});
};
function processRequestFromUser(request) {
return new Promise((resolve, reject) => {
if (!floCrypto.validateAddr(request.receiverID))
return reject(INVALID("Invalid receiverID"));
let closeNode = cloud.closestNode(request.receiverID);
if (!_list.serving.includes(closeNode))
return reject(INVALID("Incorrect Supernode"));
DB.searchData(closeNode, request)
.then(result => resolve([result]))
.catch(error => reject(error));
});
};
function processTagFromUser(data) {
return new Promise((resolve, reject) => {
if (!floCrypto.validateAddr(data.receiverID))
return reject(INVALID("Invalid receiverID"));
let closeNode = cloud.closestNode(data.receiverID);
if (!_list.serving.includes(closeNode))
return reject(INVALID("Incorrect Supernode"));
DB.getData(closeNode, data.vectorClock).then(result => {
if (!result.length)
return reject(INVALID("Invalid vectorClock"));
result = result[0];
if (!(result.application in floGlobals.appList))
return reject(INVALID("Application not authorised"));
if (!floCrypto.validateAddr(data.requestorID) ||
!floGlobals.appSubAdmins[result.application].includes(data.requestorID))
return reject(INVALID("Invalid requestorID"));
if (!floCrypto.verifyPubKey(data.pubKey, data.requestorID))
return reject(INVALID("Invalid pubKey"));
let hashcontent = ["time", "vectorClock", "tag"].map(d => data[d]).join("|");
if (!floCrypto.verifySign(hashcontent, data.sign, data.pubKey))
return reject(INVALID("Invalid signature"));
let tag = ([null, undefined, ""].includes(data.tag) ? null : data.tag.toString());
DB.tagData(closeNode, data.vectorClock, tag, data.time, data.pubKey, data.sign).then(rb => {
DB.getData(closeNode, data.vectorClock)
.then(result => resolve([result[0], 'TAG', rb]))
.catch(error => reject(error))
}).catch(error => reject(error))
}).catch(error => reject(error))
});
};
function processNoteFromUser(data) {
return new Promise((resolve, reject) => {
if (!floCrypto.validateAddr(data.receiverID))
return reject(INVALID("Invalid receiverID"));
let closeNode = cloud.closestNode(data.receiverID);
if (!_list.serving.includes(closeNode))
return reject(INVALID("Incorrect Supernode"));
DB.getData(closeNode, data.vectorClock).then(result => {
if (!result.length)
return reject(INVALID("Invalid vectorClock"));
result = result[0];
if (result.application in floGlobals.appList && floGlobals.appList[result.application] === result.receiverID) {
if (!floGlobals.appSubAdmins[result.application].includes(data.requestorID) && result.receiverID !== data.requestorID)
return reject(INVALID("Invalid requestorID"));
} else if (result.receiverID !== data.requestorID)
return reject(INVALID("Invalid requestorID"));
if (!floCrypto.verifyPubKey(data.pubKey, data.requestorID))
return reject(INVALID("Invalid pubKey"));
let hashcontent = ["time", "vectorClock", "note"].map(d => data[d]).join("|");
if (!floCrypto.verifySign(hashcontent, data.sign, data.pubKey))
return reject(INVALID("Invalid signature"));
let note = ([null, undefined, ""].includes(data.note) ? null : data.note.toString());
DB.noteData(closeNode, data.vectorClock, note, data.time, data.pubKey, data.sign).then(rb => {
DB.getData(closeNode, data.vectorClock)
.then(result => resolve([result[0], 'NOTE', rb]))
.catch(error => reject(error))
}).catch(error => reject(error))
}).catch(error => reject(error))
})
}
function checkIfRequestSatisfy(request, data) {
if (!request || request.mostRecent || request.receiverID !== (data.proxyID || data.receiverID))
return false;
if (request.atVectorClock && request.atVectorClock !== data.vectorClock)
return false;
if (request.lowerVectorClock && request.lowerVectorClock > data.vectorClock)
return false;
if (request.upperVectorClock && request.upperVectorClock < data.vectorClock)
return false;
if (request.afterTime && request.afterTime > data.log_time)
return false;
if (request.application !== data.application)
return false;
if (request.comment && request.comment !== data.comment)
return false;
if (request.type && request.type !== data.type)
return false;
if (request.senderID) {
if (Array.isArray(request.senderID)) {
if (!request.senderID.includes(data.senderID))
return false;
} else if (request.senderID !== data.senderID)
return false;
};
return true;
};
function processStatusFromUser(request, ws) {
if (request.status) {
//Set user-online status
if (!request.floID || !request.application || !request.sign || !request.pubKey || !request.time)
return ws.send("Invalid request parameters");
if (!floCrypto.verifyPubKey(request.pubKey, request.floID))
return ws.send("Invalid pubKey");
let hashcontent = ["time", "application", "floID"].map(d => request[d]).join("|");
if (!floCrypto.verifySign(hashcontent, request.sign, request.pubKey))
return ws.send("Invalid signature");
clientOnline(ws, request.application, request.floID);
} else {
//Track online status
if (!request.application || !Array.isArray(request.trackList))
return ws.send("Invalid request parameters");
addRequestClient(ws, request.application, request.trackList);
}
}
let onlineClients = {},
requestClients = {};
const ONLINE = 1,
OFFLINE = 0;
function clientOnline(ws, application, floID) {
if (!(application in onlineClients))
onlineClients[application] = {};
if (floID in onlineClients[application])
onlineClients[application][floID] += 1;
else {
onlineClients[application][floID] = 1;
informRequestClients(application, floID, ONLINE);
}
ws.on('close', () => clientOffline(application, floID));
}
function clientOffline(application, floID) {
if (application in onlineClients)
if (floID in onlineClients[application]) {
if (onlineClients[application][floID] > 1)
onlineClients[application][floID] -= 1;
else {
delete onlineClients[application][floID];
informRequestClients(application, floID, OFFLINE);
if (!Object.keys(onlineClients[application]).length)
delete onlineClients[application];
}
}
}
function addRequestClient(ws, application, trackList) {
let id = Date.now() + floCrypto.randString(8);
if (!(application in requestClients))
requestClients[application] = {};
requestClients[application][id] = {
ws: ws,
trackList
};
let status = {};
if (application in onlineClients)
trackList.forEach(floID => status[floID] = (onlineClients[application][floID] ? ONLINE : OFFLINE));
else
trackList.forEach(floID => status[floID] = OFFLINE);
ws.send(JSON.stringify(status));
ws.on('close', () => rmRequestClient(application, id));
}
function rmRequestClient(application, id) {
if ((application in requestClients) && (id in requestClients[application])) {
delete requestClients[application][id];
if (!Object.keys(requestClients[application]).length)
delete requestClients[application];
}
}
function informRequestClients(application, floID, status) {
if (application in requestClients)
for (let r in requestClients[application])
if (requestClients[application][r].trackList.includes(floID))
requestClients[application][r].ws.send(JSON.stringify({
[floID]: status
}));
}
module.exports = {
checkIfRequestSatisfy,
processRequestFromUser,
processIncomingData,
processStatusFromUser
};