- 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
277 lines
11 KiB
JavaScript
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
|
|
}; |