student-queue-mysql-plugin
Version:
MySQL-compatible plugin for my student-queue program
426 lines (370 loc) • 15.2 kB
JavaScript
/**
* Created by michael on 3/7/16.
*/
var mysql = require("mysql");
var Promise = require("promise");
var crypto = require("crypto");
function checkName(name){
if (typeof name !== "string" || name.toLowerCase() === "queues") {
return false;
}
else{
var valid = /(^\w)\w+/;
return valid.test(name);
}
}
function RequestDB(obj) {
if (this instanceof RequestDB){
var that = this;
if (!(obj.hasOwnProperty("host") && obj.hasOwnProperty("user") && obj.hasOwnProperty("password") &&
obj.hasOwnProperty("database") && obj.hasOwnProperty("table"))) {
throw new Error("Missing one or more of the required options: host, user, password, database, table")
}
if (!checkName(obj.table)){
throw new Error("Invalid table name");
}
that.table = mysql.escapeId(obj.table);
that.pool = mysql.createPool(obj);
return Object.freeze(that);
}
else return new RequestDB(obj);
}
var queueTableConfig = "(" +
"`studentid` varchar(15) NOT NULL, " +
"`name` varchar(255) NOT NULL, " +
"`description` varchar(1000) NOT NULL, " +
"`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
"PRIMARY KEY (`studentid`)" +
") ENGINE=MyISAM DEFAULT CHARSET=latin1";
RequestDB.prototype.reset = function(){
var that = this;
return new Promise(function (resolve, reject) {
that.pool.getConnection(function (err, connection) {
if (err) {
reject(err);
return;
}
console.log("Dropping table");
connection.query("TRUNCATE " + that.table, function (err) {
if (err) reject(err);
else resolve();
});
connection.release();
});
});
};
RequestDB.prototype.add = function(request){
var that = this;
if (!(request.hasOwnProperty("name") && request.hasOwnProperty("id") && request.hasOwnProperty("problem"))){
return new Promise(function (resolve, reject) {
reject(new Error("Missing one of the required properties: name, id, problem"));
});
}
return new Promise(function (resolve, reject) {
that.pool.getConnection(function (err, connection) {
if (err) {
reject(err);
return;
}
connection.query("SELECT * FROM " + that.table + " WHERE `studentid` = ? LIMIT 1",
[request.id], function (err, result) {
if (err) {
reject(err);
return;
}
if (result.length > 0) {
reject(new Error("Record with key already exists"));
}
else {
connection.query("INSERT INTO " + that.table + " (`studentid`, `name`, `description`) VALUES (?, ?, ?) ",
[request.id, request.name, request.problem], function (err, result) {
if (err) reject(err);
else resolve();
});
}
});
connection.release();
});
});
};
RequestDB.prototype.remove = function (id) {
var that = this;
return new Promise(function (resolve, reject) {
that.pool.getConnection(function (err, connection) {
if (err) {
reject(err);
return;
}
connection.query("DELETE FROM " + that.table + "WHERE `studentid` = ?", [id], function (err, result) {
if (err) reject(err);
else resolve();
});
connection.release();
});
});
};
RequestDB.prototype.getAll = function () {
var that = this;
return new Promise(function (resolve, reject) {
that.pool.getConnection(function (err, connection) {
if (err) {
reject(err);
return;
}
connection.query("SELECT * FROM " + that.table + " ORDER BY timestamp ASC",
function (err, result) {
if (err) reject(err);
else resolve(result);
});
connection.release();
});
});
};
function ConfigDB(obj){
if (this instanceof ConfigDB){
var that = this;
if (!(obj.hasOwnProperty("host") && obj.hasOwnProperty("user") && obj.hasOwnProperty("password") &&
obj.hasOwnProperty("database"))) {
throw new Error("Missing one or more of the required options: host, user, password, database")
}
that.table = mysql.escapeId("queues");
that.host = obj.host;
that.user = obj.user;
that.password = obj.password;
that.database = obj.database;
// This gets set in load()
that.queues = {};
that.connection = mysql.createConnection(obj);
return Object.freeze(that);
}
else return new ConfigDB(obj);
}
ConfigDB.prototype.createConfigTable = function(){
var that = this;
return new Promise(function (resolve, reject) {
that.connection.query("CREATE TABLE IF NOT EXISTS " + that.table + " (" +
"`name` varchar(30) NOT NULL, " +
"`table_name` varchar(30) NOT NULL, " +
"`hash` varchar(44) NOT NULL, " +
"`description` varchar(1000) NULL, " +
"PRIMARY KEY (`table_name`), " +
"UNIQUE KEY `name` (`name`)" +
") ENGINE=MyISAM DEFAULT CHARSET=latin1", function (err, result) {
if (err) reject(err);
else {
that.connection.query("SELECT * FROM " + that.table + " WHERE `name` = 'admin' LIMIT 1", function (err, result) {
if (err) reject(err);
else {
if (result.length > 0) resolve();
else {
that.connection.query("INSERT INTO " + that.table +
" (`name`, `table_name`, `hash`) VALUES ('admin', 'admin', '" + hashPassword("password") + "')",
function (err) {
if (err) reject(err);
else resolve();
});
}
}
});
}
});
});
};
ConfigDB.prototype.addNewQueue = function(obj){
var that = this;
if (!(obj.hasOwnProperty("name") && obj.hasOwnProperty("password"))) {
return new Promise(function (resolve, reject) {
reject(new Error("Missing one or more of the required options: name, password"));
});
}
else {
return new Promise(function (resolve, reject) {
if (!obj.hasOwnProperty("table_name") || typeof obj.table_name !== "string") {
obj.table_name = obj.name;
}
if (!checkName(obj.table_name)) {
return new Promise(function (resolve, reject) {
reject(new Error("Invalid table name: " + obj.table_name));
});
}
that.connection.query("SELECT * FROM " + that.table + " WHERE `name` = ? LIMIT 1",
[obj.name], function (err, result) {
if (err) {
reject(err);
return;
}
if (result.length > 0) {
reject(new Error("Queue with name " + obj.name + " already exists"));
return;
}
else {
that.connection.query("CREATE TABLE ?? " + queueTableConfig,
[obj.table_name], function (err, result) {
if (err) {
reject(err);
}
else {
var hasDesc = obj.hasOwnProperty("description");
var sql = "INSERT INTO " + that.table + " (`name`, `hash`, `table_name`" +
(hasDesc ? ", `description`" : "") +
") VALUES (?, ?, ?" +
(hasDesc ? ", ?" : "") + ") ";
var inserts = [obj.name, hashPassword(obj.password), obj.table_name];
if (hasDesc) {
inserts.push(obj.description)
}
sql = mysql.format(sql, inserts);
that.connection.query(sql, function (err, result) {
if (err) reject(err);
else {
that.load();
resolve();
}
});
}
});
}
});
});
}
};
function updateArg(name, arg, val, isNullable, that) {
return new Promise(function (resolve, reject) {
if (typeof name !== "string" || (!isNullable && typeof val !== "string")) {
reject(new Error("Missing one of the required arguments: name, " + arg));
return;
}
that.connection.query("UPDATE " + that.table + " SET ?? = ? WHERE `name` = ? LIMIT 1",
[arg, val, name], function (err, result) {
if (err) reject(err);
else resolve();
});
});
}
function hashPassword(password) {
var pwHash = crypto.createHash('sha256');
pwHash.update(password);
return pwHash.digest("base64");
}
ConfigDB.prototype.setHash = function (name, password) {
return updateArg(name, "hash", hashPassword(password), false, this);
};
ConfigDB.prototype.setQueueName = function (oldName, newName) {
return new Promise(function (resolve, reject) {
var that = this;
if (typeof oldName !== "string" || typeof newName !== "string") {
reject(new Error("Missing valid options: oldName, newName"));
return;
}
that.connection.query("SELECT * FROM " + that.table + " WHERE `name` = ? LIMIT 1",
[oldName], function (err, result) {
if (result.length > 0) {
if (oldName == newName) {
resolve();
}
else if (!checkName(newName)) {
reject(new Error("New name does not meet requirements: alphanumeric or underscore only"));
}
else {
that.connection.query("SELECT * FROM " + that.table + " WHERE `name` = ? LIMIT 1",
[newName], function (err, result) {
if (result.length === 0) {
that.connection.query("UPDATE " + that.table + "SET `name` = ? WHERE `name` = ?",
[newName, oldName], function (err, result) {
if (err) reject(err);
else resolve();
});
}
else reject(new Error("Queue with name " + newName + " already exists"));
});
}
}
else reject(new Error("Queue with name " + oldName + " does not exist"));
});
});
};
ConfigDB.prototype.setDescription = function (name, desc) {
if (desc === "") desc = null;
return updateArg(name, "description", desc, true, this);
};
ConfigDB.prototype._getHash = function (name) {
var that = this;
return new Promise(function (resolve, reject) {
if (that.queues.hasOwnProperty(name) || name === "admin") {
that.connection.query("SELECT `hash` FROM " + that.table + " WHERE `name` = ? LIMIT 1",
[name], function (err, result) {
if (err) reject(err);
else resolve(result[0].hash);
});
}
else {
reject(new Error("No queue found with name " + name));
}
});
};
ConfigDB.prototype.deleteQueue = function (name, table_name) {
var that = this;
return new Promise(function (resolve, reject) {
table_name = (table_name === undefined ? name : table_name);
that.connection.query("DROP TABLE ??", [table_name], function (err, result) {
if (err) reject(err);
else {
that.connection.query("DELETE FROM " + that.table + " WHERE `name` = ?",
[name], function (err, result) {
if (err) reject(err);
else resolve();
});
}
});
});
};
ConfigDB.prototype.getAllQueues = function () {
var that = this;
return new Promise(function (resolve, reject) {
that.connection.query("SELECT `name`, `description` FROM " + that.table + " WHERE `name` != 'admin' ORDER BY `name` ASC",
function (err, result) {
if (err) reject(err);
else resolve(result);
});
});
};
ConfigDB.prototype.load = function () {
var that = this;
return new Promise(function (resolve, reject) {
that.connection.query("SELECT `name`, `table_name` FROM " + that.table + " WHERE `name` != 'admin' ORDER BY `name` DESC",
function (err, result) {
if (err) reject(err);
else {
that.queues = {};
result.forEach(function (queue, i, arr) {
console.log(queue);
that.queues[queue.name] = new RequestDB({
host: that.host,
user: that.user,
password: that.password,
database: that.database,
table: queue.table_name
});
});
resolve();
}
});
});
};
ConfigDB.prototype.validatePassword = function (queueName, password) {
var that = this;
return new Promise(function (resolve, reject) {
var getHash = that._getHash(queueName);
getHash.then(
function (hash) {
var newHash = hashPassword(password);
if (hash === newHash) resolve();
else reject(new Error("Passwords did not match"));
},
function (err) {
reject(err);
}
);
});
};
module.exports = ConfigDB;