- Adding setup files to configure, set password. - Private key will now be stored in encrypted shares. Password will be required for running the server. - Automated creation of MySQL schema. - Making floGlobals common for both server and client. - Fixed a minor bug in database.js
494 lines
21 KiB
HTML
494 lines
21 KiB
HTML
<html>
|
|
|
|
<head>
|
|
<style>
|
|
table td,
|
|
table th {
|
|
border: 1px solid black;
|
|
}
|
|
</style>
|
|
<script src="floGlobals.js"></script>
|
|
<script src="https://sairajzero.github.io/Standard_Operations/cdn/floCrypto.js"></script>
|
|
<script src="https://github.com/sairajzero/Standard_Operations/releases/download/test/floBlockchainAPI.js"></script>
|
|
<script src="fn.js"></script>
|
|
</head>
|
|
|
|
<body>
|
|
<div>Current FLO Rate: <span id="cur-rate"></span></div>
|
|
<form id="login-form">
|
|
<fieldset>
|
|
<legend>Login</legend>
|
|
<input type="password" name="priv-key" placeholder="Enter Private Key" />
|
|
<input type="text" name="sid" style="display: none;" />
|
|
<input type="button" name="login" value="login" onclick="UI_evt.login();" /><br />
|
|
<input type="checkbox" name="remember-me" checked />RememberMe <br />
|
|
<button type="button" onclick="UI_evt.signup();">Not registered? click here!</button>
|
|
</fieldset>
|
|
</form>
|
|
<div id="user-container">
|
|
<fieldset>
|
|
<legend>Profile</legend>
|
|
<span id="user_id"></span><br />
|
|
FLO: <span id="flo_bal"></span><br />
|
|
Rupee: <span id="rupee_bal"></span><br />
|
|
<button onclick="proxy.lock();">Add password lock</button><br />
|
|
<button onclick="UI_evt.logout();">logout</button>
|
|
<form id="buy-form">
|
|
<fieldset>
|
|
<legend>Buy</legend>
|
|
<input type="number" name="quantity" placeholder="Enter Quantity" />
|
|
<input type="number" name="max-price" placeholder="Enter Max Price" />
|
|
<input type="button" name="buy" value="buy" onclick="UI_evt.buy();" />
|
|
</fieldset>
|
|
</form>
|
|
<form id="sell-form">
|
|
<fieldset>
|
|
<legend>Sell</legend>
|
|
<input type="number" name="quantity" placeholder="Enter Quantity" />
|
|
<input type="number" name="min-price" placeholder="Enter Min Price" />
|
|
<input type="button" name="sell" value="sell" onclick="UI_evt.sell();" />
|
|
</fieldset>
|
|
</form>
|
|
<form id="deposit-withdraw-form">
|
|
<fieldset>
|
|
<legend>Deposit/Withdraw</legend><br />
|
|
<input type="number" name="quantity" placeholder="Enter Quantity" />
|
|
<input type="button" name="deposit-flo" value="Deposit FLO" onclick="UI_evt.depositFLO();" />
|
|
<input type="button" name="withdraw-flo" value="Withdraw FLO" onclick="UI_evt.withdrawFLO();" />
|
|
<input type="button" name="deposit-rupee" value="Deposit Rupee" onclick="UI_evt.depositRupee();" />
|
|
<input type="button" name="withdraw-rupee" value="Withdraw Rupee" onclick="UI_evt.withdrawRupee();" />
|
|
</fieldset>
|
|
</form>
|
|
<button onclick="toggle_view('my-profile');">Toggle</button>
|
|
<div id="my-profile">
|
|
<div id="my-orders">
|
|
<fieldset>
|
|
<legend>My Orders</legend>
|
|
<fieldset id="my-buy-orders">
|
|
<legend>Buying</legend>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Select</th>
|
|
<th>Quantity</th>
|
|
<th>Max Price</th>
|
|
<th>Order Placed</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody data-type="buy"></tbody>
|
|
</table>
|
|
</fieldset>
|
|
<fieldset id="my-sell-orders">
|
|
<legend>Selling</legend>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Select</th>
|
|
<th>Quantity</th>
|
|
<th>Min Price</th>
|
|
<th>Order Placed</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody data-type="sell"></tbody>
|
|
</table>
|
|
</fieldset>
|
|
<button name="cancel-orders" onclick="UI_evt.cancelOrders();">Cancel Orders</button>
|
|
</fieldset>
|
|
|
|
</div>
|
|
<div id="my-transactions">
|
|
<fieldset>
|
|
<legend>My Transactions</legend>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Sold/Brought</th>
|
|
<th>To/From</th>
|
|
<th>Quantity</th>
|
|
<th>Unit Value</th>
|
|
<th>Time</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</fieldset>
|
|
</div>
|
|
</div>
|
|
</fieldset>
|
|
</div>
|
|
<button onclick="toggle_view('all-container');">Toggle All</button>
|
|
<button onclick="refresh();">Refresh</button>
|
|
<div id="all-container">
|
|
<div id="buy-orders">
|
|
<fieldset>
|
|
<legend>BuyOrders</legend>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Buyer</th>
|
|
<th>Quantity</th>
|
|
<th>Max Price</th>
|
|
<th>Order Placed</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</fieldset>
|
|
</div>
|
|
<div id="sell-orders">
|
|
<fieldset>
|
|
<legend>SellOrders</legend>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Seller</th>
|
|
<th>Quantity</th>
|
|
<th>Min Price</th>
|
|
<th>Order Placed</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</fieldset>
|
|
</div>
|
|
<div id="transactions">
|
|
<fieldset>
|
|
<legend>Transactions</legend>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Seller</th>
|
|
<th>Buyer</th>
|
|
<th>Quantity</th>
|
|
<th>Unit Value</th>
|
|
<th>Time</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</fieldset>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
var user_id; //container for user ID and proxy private-key
|
|
|
|
const proxy = {
|
|
private: null,
|
|
public: null,
|
|
lock() {
|
|
if (!this.private)
|
|
throw "No proxy key found!";
|
|
let pwd = prompt("Enter password: ");
|
|
if (!pwd)
|
|
alert("Password cannot be empty");
|
|
else if (pwd.length < 4)
|
|
alert("Password minimum length is 4");
|
|
else {
|
|
let tmp = Crypto.AES.encrypt(this.private, pwd);
|
|
localStorage.setItem("proxy_secret", "?" + tmp);
|
|
alert("Successfully locked with Password");
|
|
}
|
|
},
|
|
clear() {
|
|
localStorage.removeItem("proxy_secret");
|
|
this.private = null;
|
|
this.public = null;
|
|
},
|
|
set secret(key) {
|
|
localStorage.setItem("proxy_secret", key);
|
|
this.private = key;
|
|
this.public = floCrypto.getPubKeyHex(key);
|
|
},
|
|
get secret() {
|
|
if (this.private)
|
|
return this.private;
|
|
try {
|
|
let tmp = localStorage.getItem("proxy_secret");
|
|
if (typeof tmp === "string" && tmp.startsWith("?")) {
|
|
let pwd = prompt("Enter password: ");
|
|
if (!pwd)
|
|
throw "Password Required for making transactions";
|
|
else {
|
|
try {
|
|
tmp = Crypto.AES.decrypt(tmp.substring(1), pwd);
|
|
} catch (error) {
|
|
throw "Incorrect Password! Password Required for making transactions";
|
|
}
|
|
|
|
}
|
|
}
|
|
this.private = tmp;
|
|
this.public = floCrypto.getPubKeyHex(tmp);
|
|
return this.private;
|
|
} catch (error) {
|
|
alert(error);
|
|
console.error(error);
|
|
throw "Unable to fetch Proxy secret";
|
|
}
|
|
}
|
|
}
|
|
|
|
function toggle_view(id) {
|
|
let element = document.getElementById(id);
|
|
if (element.style.display === "none")
|
|
element.style.display = "block";
|
|
else
|
|
element.style.display = "none";
|
|
}
|
|
|
|
function list_buy() {
|
|
getBuyList().then(list => {
|
|
let container = document.getElementById("buy-orders").getElementsByTagName("tbody")[0];
|
|
container.innerHTML = '';
|
|
list.forEach(o => {
|
|
let row = container.insertRow();
|
|
row.insertCell().textContent = o.floID;
|
|
row.insertCell().textContent = o.quantity;
|
|
row.insertCell().textContent = o.maxPrice;
|
|
row.insertCell().textContent = new Date(o.time_placed);
|
|
row.dataset["id"] = o.id;
|
|
});
|
|
}).catch(error => console.error(error))
|
|
}
|
|
|
|
function list_sell() {
|
|
getSellList().then(list => {
|
|
let container = document.getElementById("sell-orders").getElementsByTagName("tbody")[0];
|
|
container.innerHTML = '';
|
|
list.forEach(o => {
|
|
let row = container.insertRow();
|
|
row.insertCell().textContent = o.floID;
|
|
row.insertCell().textContent = o.quantity;
|
|
row.insertCell().textContent = o.minPrice;
|
|
row.insertCell().textContent = new Date(o.time_placed);
|
|
row.dataset["id"] = o.id;
|
|
});
|
|
}).catch(error => console.error(error))
|
|
}
|
|
|
|
function list_txns() {
|
|
getTransactionList().then(list => {
|
|
let container = document.getElementById("transactions").getElementsByTagName("tbody")[0];
|
|
container.innerHTML = '';
|
|
list.forEach(o => {
|
|
let row = container.insertRow();
|
|
row.insertCell().textContent = o.seller;
|
|
row.insertCell().textContent = o.buyer;
|
|
row.insertCell().textContent = o.quantity;
|
|
row.insertCell().textContent = o.unitValue;
|
|
row.insertCell().textContent = new Date(o.tx_time);
|
|
});
|
|
}).catch(error => console.error(error))
|
|
}
|
|
|
|
function get_rate() {
|
|
getRate().then(rate => {
|
|
console.log("Rate: ", rate);
|
|
let container = document.getElementById("cur-rate");
|
|
container.textContent = "Rs " + parseFloat(rate).toFixed(2);
|
|
}).catch(error => console.error(error))
|
|
}
|
|
|
|
function refresh(init = false) {
|
|
if (init)
|
|
console.info("init");
|
|
else
|
|
console.info("refresh");
|
|
list_buy();
|
|
list_sell();
|
|
list_txns();
|
|
get_rate();
|
|
if (init || document.getElementById('user-container').style.display === "block")
|
|
account();
|
|
}
|
|
|
|
function account() {
|
|
getAccount().then(acc => {
|
|
console.debug(acc);
|
|
//Element display
|
|
document.getElementById("login-form").style.display = "none";
|
|
document.getElementById('user-container').style.display = "block";
|
|
document.getElementById("user_id").textContent = acc.floID;
|
|
user_id = acc.floID;
|
|
//FLO Balance
|
|
let flo_total = acc.coins.reduce((a, x) => a + x.quantity, 0);
|
|
let flo_locked = acc.sellOrders.reduce((a, x) => a + x.quantity, 0);
|
|
let flo_net = flo_total - flo_locked;
|
|
console.debug("FLO", flo_total, flo_locked, flo_net);
|
|
document.getElementById("flo_bal").textContent = flo_net + "(+" + flo_locked + ")";
|
|
//Rupee Balance
|
|
let rupee_total = acc.rupee_total;
|
|
let rupee_locked = acc.buyOrders.reduce((a, x) => a + x.quantity * x.maxPrice, 0);
|
|
let rupee_net = rupee_total - rupee_locked;
|
|
console.debug("RUPEE", rupee_total, rupee_locked, rupee_net);
|
|
document.getElementById("rupee_bal").textContent = rupee_net + "(+" + rupee_locked + ")";
|
|
//My buy orders
|
|
let container = document.getElementById("my-buy-orders").getElementsByTagName("tbody")[0];
|
|
container.innerHTML = '';
|
|
acc.buyOrders.forEach(o => {
|
|
let row = container.insertRow();
|
|
row.insertCell().innerHTML = `<input type="checkbox">`;
|
|
row.insertCell().textContent = o.quantity;
|
|
row.insertCell().textContent = o.maxPrice;
|
|
row.insertCell().textContent = new Date(o.time_placed);
|
|
row.dataset["id"] = o.id;
|
|
});
|
|
//My sell orders
|
|
container = document.getElementById("my-sell-orders").getElementsByTagName("tbody")[0];
|
|
container.innerHTML = '';
|
|
acc.sellOrders.forEach(o => {
|
|
let row = container.insertRow();
|
|
row.insertCell().innerHTML = `<input type="checkbox">`;
|
|
row.insertCell().textContent = o.quantity;
|
|
row.insertCell().textContent = o.minPrice;
|
|
row.insertCell().textContent = new Date(o.time_placed);
|
|
row.dataset["id"] = o.id;
|
|
});
|
|
//My Transactions
|
|
container = document.getElementById("my-transactions").getElementsByTagName("tbody")[0];
|
|
container.innerHTML = '';
|
|
acc.transactions.forEach(o => {
|
|
let type, other;
|
|
if (o.seller === acc.floID) {
|
|
type = 'Sold To';
|
|
other = o.buyer === acc.floID ? 'MySelf' : o.buyer;
|
|
} else if (o.buyer === acc.floID) {
|
|
type = 'Brought From';
|
|
other = o.seller;
|
|
} else
|
|
return;
|
|
let row = container.insertRow();
|
|
row.insertCell().textContent = type;
|
|
row.insertCell().textContent = other;
|
|
row.insertCell().textContent = o.quantity;
|
|
row.insertCell().textContent = o.unitValue;
|
|
row.insertCell().textContent = new Date(o.tx_time);
|
|
});
|
|
try {
|
|
proxy.secret;
|
|
} catch (error) {
|
|
console.warn(error);
|
|
}
|
|
}).catch(error => {
|
|
if (error instanceof ResponseError) {
|
|
let response = JSON.parse(error.data)
|
|
console.log(error);
|
|
console.log(response);
|
|
document.getElementById('user-container').style.display = "none";
|
|
document.getElementById("login-form").style.display = "block";
|
|
document.forms['login-form']["sid"].value = response.sid;
|
|
proxy.clear();
|
|
} else
|
|
console.error(error);
|
|
})
|
|
};
|
|
|
|
const UI_evt = {};
|
|
|
|
UI_evt.signup = function() {
|
|
let sid = document.forms['login-form']['sid'].value;
|
|
let privKey = prompt("Enter Private Key of floID to register: ");
|
|
signUp(privKey, sid).then(result => {
|
|
console.info(result);
|
|
alert("Account registered!")
|
|
}).catch(error => {
|
|
console.error(error)
|
|
alert(error);
|
|
});
|
|
};
|
|
|
|
UI_evt.logout = function() {
|
|
logout().then(result => {
|
|
console.warn(result);
|
|
proxy.clear();
|
|
location.reload();
|
|
}).catch(error => console.error(error));
|
|
};
|
|
|
|
UI_evt.login = function() {
|
|
let formInputs = document.forms['login-form'];
|
|
let privKey = formInputs['priv-key'].value;
|
|
let sid = formInputs['sid'].value;
|
|
let rememberMe = formInputs['remember-me'].checked;
|
|
let tmpKey = floCrypto.generateNewID();
|
|
login(privKey, tmpKey.pubKey, sid, rememberMe).then(result => {
|
|
console.log(result);
|
|
proxy.secret = tmpKey.privKey;
|
|
account();
|
|
}).catch(error => console.error(error));
|
|
};
|
|
|
|
UI_evt.sell = function() {
|
|
let formInputs = document.forms['sell-form'];
|
|
sell(parseFloat(formInputs["quantity"].value), parseFloat(formInputs["min-price"].value), proxy.secret)
|
|
.then(result => console.log(result))
|
|
.catch(error => console.error(error))
|
|
.finally(_ => formInputs.reset());
|
|
};
|
|
|
|
UI_evt.buy = function() {
|
|
let formInputs = document.forms['buy-form'];
|
|
buy(parseFloat(formInputs["quantity"].value), parseFloat(formInputs["max-price"].value), proxy.secret)
|
|
.then(result => console.log(result))
|
|
.catch(error => console.error(error))
|
|
.finally(_ => formInputs.reset());
|
|
};
|
|
|
|
UI_evt.cancelOrders = function() {
|
|
let container = document.getElementById('my-orders');
|
|
let cancel = [];
|
|
let inputs = container.getElementsByTagName('input')
|
|
for (let i = 0; i < inputs.length; i++) {
|
|
if (inputs[i].type === "checkbox" && inputs[i].checked) {
|
|
let row = inputs[i].parentElement.parentElement
|
|
let id = row.dataset['id'];
|
|
let type = row.parentElement.dataset['type'];
|
|
cancel.push([type, id]);
|
|
}
|
|
}
|
|
cancel.forEach(o => cancelOrder(o[0], o[1], proxy.secret)
|
|
.then(result => console.log(result))
|
|
.catch(error => console.error(o, error)))
|
|
};
|
|
|
|
UI_evt.depositFLO = function() {
|
|
let formInputs = document.forms['deposit-withdraw-form'];
|
|
let privKey = prompt("Enter private key");
|
|
depositFLO(parseFloat(formInputs["quantity"].value), user_id, privKey, proxy.secret)
|
|
.then(result => console.log(result))
|
|
.catch(error => console.error(error))
|
|
.finally(_ => formInputs.reset());
|
|
}
|
|
|
|
UI_evt.depositRupee = function() {
|
|
let formInputs = document.forms['deposit-withdraw-form'];
|
|
let privKey = prompt("Enter private key");
|
|
depositRupee(parseFloat(formInputs["quantity"].value), user_id, privKey, proxy.secret)
|
|
.then(result => console.log(result))
|
|
.catch(error => console.error(error))
|
|
.finally(_ => formInputs.reset());
|
|
}
|
|
|
|
UI_evt.withdrawFLO = function() {
|
|
let formInputs = document.forms['deposit-withdraw-form'];
|
|
withdrawFLO(parseFloat(formInputs["quantity"].value), proxy.secret)
|
|
.then(result => console.log(result))
|
|
.catch(error => console.error(error))
|
|
.finally(_ => formInputs.reset());
|
|
}
|
|
|
|
UI_evt.withdrawRupee = function() {
|
|
let formInputs = document.forms['deposit-withdraw-form'];
|
|
withdrawRupee(parseFloat(formInputs["quantity"].value), proxy.secret)
|
|
.then(result => console.log(result))
|
|
.catch(error => console.error(error))
|
|
.finally(_ => formInputs.reset());
|
|
}
|
|
|
|
refresh(true);
|
|
</script>
|
|
|
|
</body>
|
|
|
|
</html> |