Fixed Client page bugs: - Fixed minor bugs in client side pages - client page to use floGlobals.js - moved KBucket.js to public (as its need by client page too). Fixed Server Bugs: - Added Access-Control-Allow-Origin to response headers - Fixed: trustedIDs not loaded - Fixed minor bugs (syntax errors)
506 lines
17 KiB
JavaScript
506 lines
17 KiB
JavaScript
'use strict';
|
|
|
|
const market = require("./market");
|
|
var DB, trustedIDs, secret; //container for database
|
|
|
|
global.INVALID = function(message) {
|
|
if (!(this instanceof INVALID))
|
|
return new INVALID(message);
|
|
this.message = message;
|
|
}
|
|
INVALID.e_code = 400;
|
|
|
|
global.INTERNAL = function INTERNAL(message) {
|
|
if (!(this instanceof INTERNAL))
|
|
return new INTERNAL(message);
|
|
this.message = message;
|
|
}
|
|
INTERNAL.e_code = 500;
|
|
|
|
// creating 24 hours from milliseconds
|
|
const oneDay = 1000 * 60 * 60 * 24;
|
|
const maxSessionTimeout = 60 * oneDay;
|
|
|
|
var serving;
|
|
const INVALID_SERVER_MSG = "Incorrect Server. Please connect to main server.";
|
|
|
|
function validateRequestFromFloID(request, sign, floID, proxy = true) {
|
|
return new Promise((resolve, reject) => {
|
|
if (!serving)
|
|
return reject(INVALID(INVALID_SERVER_MSG));
|
|
else if (!floCrypto.validateAddr(floID))
|
|
return reject(INVALID.e_code).send("Invalid floID");
|
|
DB.query("SELECT " + (proxy ? "session_time, proxyKey AS pubKey FROM Sessions" : "pubKey FROM Users") + " WHERE floID=?", [floID]).then(result => {
|
|
if (result.length < 1)
|
|
return reject(INVALID(proxy ? "Session not active" : "User not registered"));
|
|
if (proxy && result[0].session_time + maxSessionTimeout < Date.now())
|
|
return reject(INVALID.e_code).send("Session Expired! Re-login required");
|
|
let req_str = validateRequest(request, sign, result[0].pubKey);
|
|
req_str instanceof INVALID ? reject(req_str) : resolve(req_str);
|
|
}).catch(error => reject(error));
|
|
});
|
|
}
|
|
|
|
function validateRequest(request, sign, pubKey) {
|
|
if (typeof request !== "object")
|
|
return INVALID("Request is not an object");
|
|
let req_str = Object.keys(request).sort().map(r => r + ":" + request[r]).join("|");
|
|
try {
|
|
if (floCrypto.verifySign(req_str, sign, pubKey))
|
|
return req_str;
|
|
else
|
|
return INVALID("Invalid request signature! Re-login if this error occurs frequently");
|
|
} catch {
|
|
return INVALID("Corrupted sign/key");
|
|
}
|
|
}
|
|
|
|
function storeRequest(floID, req_str, sign) {
|
|
console.debug(floID, req_str);
|
|
DB.query("INSERT INTO Request_Log (floID, request, sign) VALUES (?,?,?)", [floID, req_str, sign])
|
|
.then(_ => null).catch(error => console.error(error));
|
|
}
|
|
|
|
function getLoginCode(req, res) {
|
|
let randID = floCrypto.randString(8, true) + Math.round(Date.now() / 1000);
|
|
let hash = Crypto.SHA1(randID + secret);
|
|
res.send({
|
|
code: randID,
|
|
hash: hash
|
|
});
|
|
}
|
|
|
|
function SignUp(req, res) {
|
|
if (!serving)
|
|
return res.status(INVALID.e_code).send(INVALID_SERVER_MSG);
|
|
let data = req.body;
|
|
if (floCrypto.getFloID(data.pubKey) !== data.floID)
|
|
return res.status(INVALID.e_code).send("Invalid Public Key");
|
|
if (!data.code || data.hash != Crypto.SHA1(data.code + secret))
|
|
return res.status(INVALID.e_code).send("Invalid Code");
|
|
let req_str = validateRequest({
|
|
type: "create_account",
|
|
random: data.code,
|
|
timestamp: data.timestamp
|
|
}, data.sign, data.pubKey);
|
|
if (req_str instanceof INVALID)
|
|
return res.status(INVALID.e_code).send(req_str.message);
|
|
let txQueries = [];
|
|
txQueries.push(["INSERT INTO Users(floID, pubKey) VALUES (?, ?)", [data.floID, data.pubKey]]);
|
|
txQueries.push(["INSERT INTO Cash (floID) Values (?)", [data.floID]]);
|
|
DB.transaction(txQueries).then(_ => {
|
|
storeRequest(data.floID, req_str, data.sign);
|
|
res.send("Account Created");
|
|
}).catch(error => {
|
|
if (error.code === "ER_DUP_ENTRY")
|
|
res.status(INVALID.e_code).send("Account already exist");
|
|
else {
|
|
console.error(error);
|
|
res.status(INTERNAL.e_code).send("Account creation failed! Try Again Later!");
|
|
}
|
|
});
|
|
}
|
|
|
|
function Login(req, res) {
|
|
let data = req.body;
|
|
if (!data.code || data.hash != Crypto.SHA1(data.code + secret))
|
|
return res.status(INVALID.e_code).send("Invalid Code");
|
|
validateRequestFromFloID({
|
|
type: "login",
|
|
random: data.code,
|
|
proxyKey: data.proxyKey,
|
|
timestamp: data.timestamp
|
|
}, data.sign, data.floID, false).then(req_str => {
|
|
DB.query("INSERT INTO Sessions (floID, proxyKey) VALUE (?, ?) AS new " +
|
|
"ON DUPLICATE KEY UPDATE session_time=DEFAULT, proxyKey=new.proxyKey",
|
|
[data.floID, data.proxyKey]).then(_ => {
|
|
storeRequest(data.floID, req_str, data.sign);
|
|
res.send("Login Successful");
|
|
}).catch(error => {
|
|
console.error(error);
|
|
res.status(INTERNAL.e_code).send("Login failed! Try again later!");
|
|
});
|
|
}).catch(error => {
|
|
if (error instanceof INVALID)
|
|
res.status(INVALID.e_code).send(error.message);
|
|
else {
|
|
console.error(error);
|
|
res.status(INTERNAL.e_code).send("Login failed! Try again later!");
|
|
}
|
|
})
|
|
}
|
|
|
|
function Logout(req, res) {
|
|
let data = req.body;
|
|
validateRequestFromFloID({
|
|
type: "logout",
|
|
timestamp: data.timestamp
|
|
}, data.sign, data.floID).then(req_str => {
|
|
DB.query("DELETE FROM Sessions WHERE floID=?", [data.floID]).then(_ => {
|
|
storeRequest(data.floID, req_str, data.sign);
|
|
res.send('Logout successful');
|
|
}).catch(error => {
|
|
console.error(error);
|
|
res.status(INTERNAL.e_code).send("Logout failed! Try again later! Contact support if this error occurs frequently");
|
|
});
|
|
}).catch(error => {
|
|
if (error instanceof INVALID)
|
|
res.status(INVALID.e_code).send(error.message);
|
|
else {
|
|
console.error(error);
|
|
res.status(INTERNAL.e_code).send("Request processing failed! Try again later!");
|
|
}
|
|
});
|
|
}
|
|
|
|
function PlaceSellOrder(req, res) {
|
|
let data = req.body;
|
|
validateRequestFromFloID({
|
|
type: "sell_order",
|
|
quantity: data.quantity,
|
|
min_price: data.min_price,
|
|
timestamp: data.timestamp
|
|
}, data.sign, data.floID).then(req_str => {
|
|
market.addSellOrder(data.floID, data.quantity, data.min_price)
|
|
.then(result => {
|
|
storeRequest(data.floID, req_str, data.sign);
|
|
res.send('Sell Order placed successfully');
|
|
}).catch(error => {
|
|
if (error instanceof INVALID)
|
|
res.status(INVALID.e_code).send(error.message);
|
|
else {
|
|
console.error(error);
|
|
res.status(INTERNAL.e_code).send("Order placement failed! Try again later!");
|
|
}
|
|
});
|
|
}).catch(error => {
|
|
if (error instanceof INVALID)
|
|
res.status(INVALID.e_code).send(error.message);
|
|
else {
|
|
console.error(error);
|
|
res.status(INTERNAL.e_code).send("Request processing failed! Try again later!");
|
|
}
|
|
});
|
|
}
|
|
|
|
function PlaceBuyOrder(req, res) {
|
|
let data = req.body;
|
|
validateRequestFromFloID({
|
|
type: "buy_order",
|
|
quantity: data.quantity,
|
|
max_price: data.max_price,
|
|
timestamp: data.timestamp
|
|
}, data.sign, data.floID).then(req_str => {
|
|
market.addBuyOrder(data.floID, data.quantity, data.max_price)
|
|
.then(result => {
|
|
storeRequest(data.floID, req_str, data.sign);
|
|
res.send('Buy Order placed successfully');
|
|
}).catch(error => {
|
|
if (error instanceof INVALID)
|
|
res.status(INVALID.e_code).send(error.message);
|
|
else {
|
|
console.error(error);
|
|
res.status(INTERNAL.e_code).send("Order placement failed! Try again later!");
|
|
}
|
|
});
|
|
}).catch(error => {
|
|
if (error instanceof INVALID)
|
|
res.status(INVALID.e_code).send(error.message);
|
|
else {
|
|
console.error(error);
|
|
res.status(INTERNAL.e_code).send("Request processing failed! Try again later!");
|
|
}
|
|
});
|
|
}
|
|
|
|
function CancelOrder(req, res) {
|
|
let data = req.body;
|
|
validateRequestFromFloID({
|
|
type: "cancel_order",
|
|
order: data.orderType,
|
|
id: data.orderID,
|
|
timestamp: data.timestamp
|
|
}, data.sign, data.floID).then(req_str => {
|
|
market.cancelOrder(data.orderType, data.orderID, data.floID)
|
|
.then(result => {
|
|
storeRequest(data.floID, req_str, data.sign);
|
|
res.send(result);
|
|
}).catch(error => {
|
|
if (error instanceof INVALID)
|
|
res.status(INVALID.e_code).send(error.message);
|
|
else {
|
|
console.error(error);
|
|
res.status(INTERNAL.e_code).send("Order cancellation failed! Try again later!");
|
|
}
|
|
});
|
|
}).catch(error => {
|
|
if (error instanceof INVALID)
|
|
res.status(INVALID.e_code).send(error.message);
|
|
else {
|
|
console.error(error);
|
|
res.status(INTERNAL.e_code).send("Request processing failed! Try again later!");
|
|
}
|
|
});
|
|
}
|
|
|
|
function ListSellOrders(req, res) {
|
|
//TODO: Limit size (best)
|
|
DB.query("SELECT * FROM SellOrder ORDER BY time_placed")
|
|
.then(result => res.send(result))
|
|
.catch(error => res.status(INTERNAL.e_code).send("Try again later!"));
|
|
}
|
|
|
|
function ListBuyOrders(req, res) {
|
|
//TODO: Limit size (best)
|
|
DB.query("SELECT * FROM BuyOrder ORDER BY time_placed")
|
|
.then(result => res.send(result))
|
|
.catch(error => res.status(INTERNAL.e_code).send("Try again later!"));
|
|
}
|
|
|
|
function ListTransactions(req, res) {
|
|
//TODO: Limit size (recent)
|
|
DB.query("SELECT * FROM Transactions ORDER BY tx_time DESC")
|
|
.then(result => res.send(result))
|
|
.catch(error => res.status(INTERNAL.e_code).send("Try again later!"));
|
|
}
|
|
|
|
function getRate(req, res) {
|
|
if (!serving)
|
|
res.status(INVALID.e_code).send(INVALID_SERVER_MSG);
|
|
else
|
|
res.send(`${market.rate}`);
|
|
}
|
|
|
|
function Account(req, res) {
|
|
let data = req.body;
|
|
validateRequestFromFloID({
|
|
type: "get_account",
|
|
timestamp: data.timestamp
|
|
}, data.sign, data.floID).then(req_str => {
|
|
market.getAccountDetails(data.floID).then(result => {
|
|
if (trustedIDs.includes(data.floID))
|
|
result.subAdmin = true;
|
|
res.send(result);
|
|
});
|
|
}).catch(error => {
|
|
if (error instanceof INVALID)
|
|
res.status(INVALID.e_code).send(error.message);
|
|
else {
|
|
console.error(error);
|
|
res.status(INTERNAL.e_code).send("Request processing failed! Try again later!");
|
|
}
|
|
});
|
|
}
|
|
|
|
function DepositFLO(req, res) {
|
|
let data = req.body;
|
|
validateRequestFromFloID({
|
|
type: "deposit_FLO",
|
|
txid: data.txid,
|
|
timestamp: data.timestamp
|
|
}, data.sign, data.floID).then(req_str => {
|
|
market.depositFLO(data.floID, data.txid).then(result => {
|
|
storeRequest(data.floID, req_str, data.sign);
|
|
res.send(result);
|
|
}).catch(error => {
|
|
if (error instanceof INVALID)
|
|
res.status(INVALID.e_code).send(error.message);
|
|
else {
|
|
console.error(error);
|
|
res.status(INTERNAL.e_code).send("Request processing failed! Try again later!");
|
|
}
|
|
});
|
|
}).catch(error => {
|
|
if (error instanceof INVALID)
|
|
res.status(INVALID.e_code).send(error.message);
|
|
else {
|
|
console.error(error);
|
|
res.status(INTERNAL.e_code).send("Request processing failed! Try again later!");
|
|
}
|
|
});
|
|
}
|
|
|
|
function WithdrawFLO(req, res) {
|
|
let data = req.body;
|
|
validateRequestFromFloID({
|
|
type: "withdraw_FLO",
|
|
amount: data.amount,
|
|
timestamp: data.timestamp
|
|
}, data.sign, data.floID).then(req_str => {
|
|
market.withdrawFLO(data.floID, data.amount).then(result => {
|
|
storeRequest(data.floID, req_str, data.sign);
|
|
res.send(result);
|
|
}).catch(error => {
|
|
if (error instanceof INVALID)
|
|
res.status(INVALID.e_code).send(error.message);
|
|
else {
|
|
console.error(error);
|
|
res.status(INTERNAL.e_code).send("Request processing failed! Try again later!");
|
|
}
|
|
});
|
|
}).catch(error => {
|
|
if (error instanceof INVALID)
|
|
res.status(INVALID.e_code).send(error.message);
|
|
else {
|
|
console.error(error);
|
|
res.status(INTERNAL.e_code).send("Request processing failed! Try again later!");
|
|
}
|
|
});
|
|
}
|
|
|
|
function DepositRupee(req, res) {
|
|
let data = req.body;
|
|
validateRequestFromFloID({
|
|
type: "deposit_Rupee",
|
|
txid: data.txid,
|
|
timestamp: data.timestamp
|
|
}, data.sign, data.floID).then(req_str => {
|
|
market.depositRupee(data.floID, data.txid).then(result => {
|
|
storeRequest(data.floID, req_str, data.sign);
|
|
res.send(result);
|
|
}).catch(error => {
|
|
if (error instanceof INVALID)
|
|
res.status(INVALID.e_code).send(error.message);
|
|
else {
|
|
console.error(error);
|
|
res.status(INTERNAL.e_code).send("Request processing failed! Try again later!");
|
|
}
|
|
});
|
|
}).catch(error => {
|
|
if (error instanceof INVALID)
|
|
res.status(INVALID.e_code).send(error.message);
|
|
else {
|
|
console.error(error);
|
|
res.status(INTERNAL.e_code).send("Request processing failed! Try again later!");
|
|
}
|
|
});
|
|
}
|
|
|
|
function WithdrawRupee(req, res) {
|
|
let data = req.body;
|
|
validateRequestFromFloID({
|
|
type: "withdraw_Rupee",
|
|
amount: data.amount,
|
|
timestamp: data.timestamp
|
|
}, data.sign, data.floID).then(req_str => {
|
|
market.withdrawRupee(data.floID, data.amount).then(result => {
|
|
storeRequest(data.floID, req_str, data.sign);
|
|
res.send(result);
|
|
}).catch(error => {
|
|
if (error instanceof INVALID)
|
|
res.status(INVALID.e_code).send(error.message);
|
|
else {
|
|
console.error(error);
|
|
res.status(INTERNAL.e_code).send("Request processing failed! Try again later!");
|
|
}
|
|
});
|
|
}).catch(error => {
|
|
if (error instanceof INVALID)
|
|
res.status(INVALID.e_code).send(error.message);
|
|
else {
|
|
console.error(error);
|
|
res.status(INTERNAL.e_code).send("Request processing failed! Try again later!");
|
|
}
|
|
});
|
|
}
|
|
|
|
function addUserTag(req, res) {
|
|
let data = req.body;
|
|
if (!trustedIDs.includes(data.floID))
|
|
return res.status(INVALID.e_code).send("Access Denied");
|
|
validateRequestFromFloID({
|
|
command: "add_Tag",
|
|
user: data.user,
|
|
tag: data.tag,
|
|
timestamp: data.timestamp
|
|
}, data.sign, data.floID).then(req_str => {
|
|
market.group.addTag(data.user, data.tag).then(result => {
|
|
storeRequest(data.floID, req_str, data.sign);
|
|
res.send(result);
|
|
}).catch(error => {
|
|
if (error instanceof INVALID)
|
|
res.status(INVALID.e_code).send(error.message);
|
|
else {
|
|
console.error(error);
|
|
res.status(INTERNAL.e_code).send("Request processing failed! Try again later!");
|
|
}
|
|
});
|
|
}).catch(error => {
|
|
if (error instanceof INVALID)
|
|
res.status(INVALID.e_code).send(error.message);
|
|
else {
|
|
console.error(error);
|
|
res.status(INTERNAL.e_code).send("Request processing failed! Try again later!");
|
|
}
|
|
});
|
|
}
|
|
|
|
function removeUserTag(req, res) {
|
|
let data = req.body;
|
|
if (!trustedIDs.includes(data.floID))
|
|
return res.status(INVALID.e_code).send("Access Denied");
|
|
else
|
|
validateRequestFromFloID({
|
|
command: "remove_Tag",
|
|
user: data.user,
|
|
tag: data.tag,
|
|
timestamp: data.timestamp
|
|
}, data.sign, data.floID).then(req_str => {
|
|
market.group.removeTag(data.user, data.tag).then(result => {
|
|
storeRequest(data.floID, req_str, data.sign);
|
|
res.send(result);
|
|
}).catch(error => {
|
|
if (error instanceof INVALID)
|
|
res.status(INVALID.e_code).send(error.message);
|
|
else {
|
|
console.error(error);
|
|
res.status(INTERNAL.e_code).send("Request processing failed! Try again later!");
|
|
}
|
|
});
|
|
}).catch(error => {
|
|
if (error instanceof INVALID)
|
|
res.status(INVALID.e_code).send(error.message);
|
|
else {
|
|
console.error(error);
|
|
res.status(INTERNAL.e_code).send("Request processing failed! Try again later!");
|
|
}
|
|
});
|
|
}
|
|
|
|
module.exports = {
|
|
getLoginCode,
|
|
SignUp,
|
|
Login,
|
|
Logout,
|
|
PlaceBuyOrder,
|
|
PlaceSellOrder,
|
|
CancelOrder,
|
|
ListSellOrders,
|
|
ListBuyOrders,
|
|
ListTransactions,
|
|
getRate,
|
|
Account,
|
|
DepositFLO,
|
|
WithdrawFLO,
|
|
DepositRupee,
|
|
WithdrawRupee,
|
|
periodicProcess: market.periodicProcess,
|
|
addUserTag,
|
|
removeUserTag,
|
|
set trustedIDs(ids) {
|
|
trustedIDs = ids;
|
|
},
|
|
set DB(db) {
|
|
DB = db;
|
|
market.DB = db;
|
|
},
|
|
set secret(s) {
|
|
secret = s;
|
|
},
|
|
pause() {
|
|
serving = false;
|
|
},
|
|
resume() {
|
|
serving = true;
|
|
}
|
|
}; |