diff --git a/lib/database.js b/lib/database.js index eb7bb944..3ab70b4a 100644 --- a/lib/database.js +++ b/lib/database.js @@ -287,104 +287,6 @@ module.exports.clearGlobalIPBan = function (ip, callback) { /* REGION channels */ -module.exports.searchChannel = function (field, value, callback) { - if(typeof callback !== "function") - return; - - var query = "SELECT * FROM channels WHERE "; - if(field === "owner") - query += "owner LIKE ?"; - else if(field === "name") - query += "name LIKE ?"; - - module.exports.query(query, ["%" + value + "%"], callback); -}; - -module.exports.channelExists = function (name, callback) { - if(typeof callback !== "function") - return; - if(!$util.isValidChannelName(name)) { - callback("Invalid channel name", null); - return; - } - - var query = "SELECT name FROM channels WHERE name=?"; - module.exports.query(query, [name], function (err, res) { - callback(err, res.length > 0); - }); -}; - -module.exports.registerChannel = function (name, owner, callback) { - if(typeof callback !== "function") - callback = blackHole; - - if(!$util.isValidChannelName(name)) { - callback("Invalid channel name", null); - return; - } - - // Messy, but I can't think of a better async solution atm - var query = "SELECT * FROM channels WHERE name=?"; - module.exports.query(query, [name], function (err, res) { - if(!err && res.length > 0) { - callback("Channel already exists", null); - return; - } - - // Library table - query = ["CREATE TABLE `chan_" + name + "_library` (", - "`id` VARCHAR(255) NOT NULL,", - "`title` VARCHAR(255) NOT NULL,", - "`seconds` INT NOT NULL,", - "`type` VARCHAR(2) NOT NULL,", - "PRIMARY KEY (`id`))", - "ENGINE = MyISAM ", - "CHARACTER SET utf8;"].join(""); - module.exports.query(query, function (err, res) { - if(err) { - callback(err, null); - return; - } - - // Rank table - query = ["CREATE TABLE `chan_" + name + "_ranks` (", - "`name` VARCHAR(32) NOT NULL,", - "`rank` INT NOT NULL,", - "UNIQUE (`name`))", - "ENGINE = MyISAM ", - "CHARACTER SET utf8;"].join(""); - - module.exports.query(query, function (err, res) { - if(err) { - callback(err, null); - return; - } - - // Ban table - query = ["CREATE TABLE `chan_" + name + "_bans` (", - "`ip` VARCHAR(15) NOT NULL,", - "`name` VARCHAR(32) NOT NULL,", - "`banner` VARCHAR(32) NOT NULL,", - "PRIMARY KEY (`ip`, `name`))", - "ENGINE = MyISAM ", - "CHARACTER SET utf8;"].join(""); - - module.exports.query(query, function (err, res) { - if(err) { - callback(err, null); - return; - } - - query = "INSERT INTO channels VALUES (NULL, ?, ?)"; - module.exports.query(query, [name, owner], function (err, res) { - callback(err, res); - }); - }); - }); - }); - }); -}; - module.exports.loadChannelData = function (chan, callback) { if(typeof callback !== "function") callback = blackHole; @@ -443,97 +345,6 @@ module.exports.loadChannelData = function (chan, callback) { }); }; -module.exports.dropChannel = function (name, callback) { - if(typeof callback !== "function") - callback = blackHole; - - if(!$util.isValidChannelName(name)) { - callback("Invalid channel name", null); - return; - } - - var query = "DROP TABLE `chan_?_bans`,`chan_?_ranks`,`chan_?_library`" - .replace(/\?/g, name); - - module.exports.query(query, function (err, res) { - if(err) { - Logger.errlog.log("! Failed to drop channel tables for "+name); - callback(err, null); - return; - } - - query = "DELETE FROM channels WHERE name=?"; - module.exports.query(query, [name], function (err, res) { - callback(err, res); - if(err) { - Logger.errlog.log("! Failed to delete channel "+name); - } - }); - }); -}; - -module.exports.getChannelRank = function (channame, name, callback) { - if(typeof callback !== "function") - return; - - if(!$util.isValidChannelName(channame)) { - callback("Invalid channel name", null); - return; - } - - var query = "SELECT name, rank FROM `chan_" + channame + "_ranks`" + - "WHERE name=?"; - - module.exports.query(query, [name], function (err, res) { - if(err) { - Logger.errlog.log("! Failed to lookup " + channame + " ranks"); - callback(err, null); - return; - } - - if(res.length == 0) - callback(null, 0); - else - callback(null, res[0].rank); - }); -}; - -module.exports.listChannelUserRanks = function (channame, names, - callback) { - if(typeof callback !== "function") - return; - - if(!$util.isValidChannelName(channame)) { - callback("Invalid channel name", null); - return; - } - - if(typeof names === "string") - names = [names]; - - // Build the query template (?, ?, ?, ?, ...) - var nlist = []; - for(var i in names) - nlist.push("?"); - nlist = "(" + nlist.join(",") + ")"; - - var query = "SELECT name, rank FROM `chan_" + channame + "_ranks`" + - "WHERE name IN " + nlist; - - module.exports.query(query, names, function (err, res) { - if(err) { - Logger.errlog.log("! Failed to lookup " + channame + " ranks"); - callback(err, null); - return; - } - - for(var i in res) - res[i] = res[i].rank; - - callback(null, res); - }); -}; - module.exports.setChannelRank = function (channame, name, rank, callback) { if(typeof callback !== "function") callback = blackHole; diff --git a/lib/database/channels.js b/lib/database/channels.js new file mode 100644 index 00000000..59582898 --- /dev/null +++ b/lib/database/channels.js @@ -0,0 +1,294 @@ +var db = require("../database"); +var $util = require("../utilities"); + +var blackHole = function () { }; + +function dropTable(name, callback) { + db.query("DROP TABLE `" + name + "`"); +} + +function createRanksTable(name, callback) { + db.query("CREATE TABLE `chan_" + name + "_ranks` (" + + "`name` VARCHAR(20) NOT NULL," + + "`rank` INT NOT NULL," + + "PRIMARY KEY (`name`)) " + + "CHARACTER SET utf8", callback); +} + +function createLibraryTable(name, callback) { + db.query("CREATE TABLE `chan_" + name + "_library` (" + + "`id` VARCHAR(255) NOT NULL," + + "`title` VARCHAR(255) NOT NULL," + + "`seconds` INT NOT NULL," + "`type` VARCHAR(2) NOT NULL," + + "PRIMARY KEY (`id`))" + + "CHARACTER SET utf8", callback); +} + +function createBansTable(name, callback) { + db.query("CREATE TABLE `chan_" + name + "_bans` (" + + "`ip` VARCHAR(39) NOT NULL," + + "`name` VARCHAR(20) NOT NULL," + + "`bannedby` VARCHAR(20) NOT NULL," + + "`reason` VARCHAR(255) NOT NULL," + + "PRIMARY KEY (`ip`, `name`))" + + "CHARACTER SET utf8", callback); +} + +function initTables(name, owner, callback) { + createRanksTable(name, function (err) { + if (err) { + callback(err, null); + return; + } + + // TODO add owner to ranks table + + createLibraryTable(name, function (err) { + if (err) { + dropTable("chan_" + name + "_ranks"); + callback(err, null); + return; + } + + createBansTable(name, function (err) { + if (err) { + dropTable("chan_" + name + "_ranks"); + dropTable("chan_" + name + "_library"); + callback(err, null); + return; + } + + callback(null, true); + }); + }); + }); +} + +module.exports = { + /** + * Initialize the channels table + */ + init: function () { + db.query("CREATE TABLE IF NOT EXISTS `channels` (" + + "`id` INT NOT NULL AUTO_INCREMENT," + + "`name` VARCHAR(30) NOT NULL," + + "`owner` VARCHAR(20) NOT NULL," + + "`time` BIGINT NOT NULL," + + "PRIMARY KEY (`id`), INDEX(`name`), INDEX(`owner`))" + + "CHARACTER SET utf8"); + }, + + /** + * Checks if the given channel name is registered + */ + isChannelTaken: function (name, callback) { + if (typeof callback !== "function") { + return; + } + + dbutil.query("SELECT name FROM `channels` WHERE name=?", + [name], + function (err, rows) { + if (err) { + callback(err, true); + return; + } + callback(null, rows.length > 0); + }); + }, + + /** + * Looks up a channel + */ + lookup: function (name, callback) { + if (typeof callback !== "function") { + return; + } + + dbutil.query("SELECT * FROM `channels` WHERE name=?", + [name], + function (err, rows) { + if (err) { + callback(err, null); + return; + } + callback(null, rows); + }); + }, + + /** + * Searches for a channel + */ + search: function (name, callback) { + if (typeof callback !== "function") { + return; + } + + dbutil.query("SELECT * FROM `channels` WHERE name LIKE ?", + ["%" + name + "%"], + function (err, rows) { + if (err) { + callback(err, null); + return; + } + callback(null, rows); + }); + }, + + /** + * Validates and registers a new channel + */ + register: function (name, owner, callback) { + if (typeof callback !== "function") { + callback = blackHole; + } + + if (typeof name !== "string" || typeof owner !== "string") { + callback("Name and owner are required for channel registration", null); + return; + } + + if (!$util.isValidChannelName(name)) { + callback("Invalid channel name. Channel names may consist of 1-30 " + + "characters a-z, A-Z, 0-9, -, and _", null); + return; + } + + module.exports.isChannelTaken(name, function (err, taken) { + if (err) { + callback(err, null); + return; + } + + if (taken) { + callback("Channel name " + name + " is already taken", null); + return; + } + + dbutil.query("INSERT INTO `channels` " + + "(`name`, `owner`, `time`) VALUES (?, ?, ?)", + [name, owner, Date.now()], + function (err, res) { + if (err) { + callback(err, null); + return; + } + + initTables(name, owner, function (err, res) { + if (err) { + dbutil.query("DELETE FROM `channels` WHERE name=?", [name]); + callback(err, null); + return; + } + callback(null, { + name: name + }); + }); + }); + }); + }, + + /** + * Unregisters a channel + */ + drop: function (name, callback) { + if (typeof callback !== "function") { + callback = blackHole; + } + + dropTable("chan_" + name + "_ranks", function (err) { + dropTable("chan_" + name + "_bans", function (e2) { + if (err && e2) { + err += "\n" + e2; + } else if (e2) { + err = e2; + } + + dropTable("chan_" + name + "_library", function (e3) { + if (err && e3) { + err += "\n" + e3; + } else if (e3) { + err = e3; + } + + db.query("DELETE FROM `channels` WHERE name=?", [name], + function (e4) { + if (err && e4) { + err += "\n" + e4; + } else if (e4) { + err = e4; + } + + callback(err, !Boolean(err)); + } + }); + }); + }); + }, + + /** + * Looks up channels registered by a given user + */ + listUserChannels: function (owner, callback) { + if (typeof callback !== "function") { + return; + } + + db.query("SELECT * FROM `channels` WHERE owner=?", [owner], + function (err, res) { + if (err) { + callback(err, []); + return; + } + + callback(err, res); + }); + }, + + /** + * Looks up a user's rank + */ + getRank: function (chan, name, callback) { + if (typeof callback !== "function") { + return; + } + + db.query("SELECT name,rank FROM `chan_" + chan + "_ranks` WHERE name=?", + [name], + function (err, rows) { + if (err) { + callback(err, 1); + return; + } + + if (rows.length === 0) { + callback(null, 1); + return; + } + + callback(null, rows[0].rank); + }); + }, + + /** + * Looks up multiple users' ranks at once + */ + getRanks: function (chan, names, callback) { + if (typeof callback !== "function") { + return; + } + + var replace = "(" + names.map(function () { return "?"; }).join(",") + ")"; + db.query("SELECT name,rank FROM `chan_" + chan + "_ranks` WHERE name IN " + + replace, + function (err, rows) { + if (err) { + callback(err, []); + return; + } + + callback(null, rows); + }); + }, +};