diff --git a/README.md b/README.md
index 3c905931..0d550ba5 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ I'm using v0.8.20, please feel free to report which versions do/do not work
5. Install your distribution's `libmysqlclient` package
6. Install the libmysql node module: `npm install mysql-libmysqlclient`
7. Edit `config.js` and input your database details and connection port
- 8. Edit `www/assets/js/client.js` and change the value of `IO_URL` to `yourhostname:port` where `port` is the port defined in `config.js`
+ 8. Edit `www/assets/js/iourl.js` and change the value of `IO_URL` to `yourhostname:port` where `port` is the port defined in `config.js`
Running
-------
diff --git a/database.js b/database.js
new file mode 100644
index 00000000..02f4be19
--- /dev/null
+++ b/database.js
@@ -0,0 +1,101 @@
+var mysql = require('mysql-libmysqlclient');
+var Config = require('./config.js');
+
+var initialized = false;
+
+exports.init = function() {
+ if(initialized)
+ return;
+
+ var db = mysql.createConnectionSync();
+ db.connectSync(Config.MYSQL_SERVER, Config.MYSQL_USER,
+ Config.MYSQL_PASSWORD, Config.MYSQL_DB);
+ var query = "CREATE TABLE IF NOT EXISTS `channels` \
+ (`id` INT NOT NULL, \
+ `name` VARCHAR(255) NOT NULL, \
+ PRIMARY KEY (`id`)) \
+ ENGINE = MyISAM;";
+ var results = db.querySync(query);
+ if(!results) {
+ console.log("Database initialization failed! Could not create channel table");
+ return false;
+ }
+
+ var query = "CREATE TABLE IF NOT EXISTS `registrations` \
+ (`id` INT NOT NULL, \
+ `uname` VARCHAR(20) NOT NULL, \
+ `pw` VARCHAR(64) NOT NULL, \
+ `global_rank` INT NOT NULL, \
+ PRIMARY KEY (`id`)) \
+ ENGINE = MyISAM;";
+ var results = db.querySync(query);
+ if(!results) {
+ console.log("Database initialization failed! Could not create registration table");
+ return false;
+ }
+
+ initialized = true;
+ return true;
+}
+
+exports.listChannels = function() {
+ if(!initialized)
+ return false;
+ var db = mysql.createConnectionSync();
+ db.connectSync(Config.MYSQL_SERVER, Config.MYSQL_USER,
+ Config.MYSQL_PASSWORD, Config.MYSQL_DB);
+ var query = "SELECT * FROM `channels`";
+ var results = db.querySync(query);
+ if(!results) {
+ console.log("Database channel listing failed!");
+ return false;
+ }
+
+ if(results) {
+ var rows = results.fetchAllSync();
+ db.closeSync();
+ return rows;
+ }
+};
+
+exports.listUsers = function() {
+ if(!initialized)
+ return false;
+ var db = mysql.createConnectionSync();
+ db.connectSync(Config.MYSQL_SERVER, Config.MYSQL_USER,
+ Config.MYSQL_PASSWORD, Config.MYSQL_DB);
+ var query = "SELECT * FROM `registrations`";
+ var results = db.querySync(query);
+ if(!results) {
+ console.log("Database user listing failed!");
+ return false;
+ }
+
+ if(results) {
+ var rows = results.fetchAllSync();
+ db.closeSync();
+ return rows;
+ }
+};
+
+exports.listChannelRanks = function(chan) {
+ if(!initialized)
+ return false;
+ var db = mysql.createConnectionSync();
+ db.connectSync(Config.MYSQL_SERVER, Config.MYSQL_USER,
+ Config.MYSQL_PASSWORD, Config.MYSQL_DB);
+ var query = "SELECT * FROM `chan_{}_ranks`"
+ .replace(/\{\}/, chan);
+ console.log(query);
+ var results = db.querySync(query);
+ if(!results) {
+ console.log("Database channel listing failed!");
+ return false;
+ }
+
+ if(results) {
+ var rows = results.fetchAllSync();
+ db.closeSync();
+ return rows;
+ }
+};
diff --git a/rank.js b/rank.js
index a4257d32..5358b015 100644
--- a/rank.js
+++ b/rank.js
@@ -13,6 +13,7 @@ exports.Owner = 3;
exports.Siteadmin = 255;
var permissions = {
+ acp: exports.Siteadmin,
queue: exports.Moderator,
assignLeader: exports.Moderator,
kick: exports.Moderator,
diff --git a/server.js b/server.js
index e813639a..d721f4b0 100644
--- a/server.js
+++ b/server.js
@@ -11,6 +11,8 @@ var Config = require('./config.js');
var connect = require('connect');
var app = connect.createServer(connect.static(__dirname+'/www')).listen(Config.IO_PORT);
var io = require('socket.io').listen(app);
+var Database = require('./database.js');
+Database.init();
exports.channels = {};
diff --git a/user.js b/user.js
index 3d9b281f..212546d2 100644
--- a/user.js
+++ b/user.js
@@ -10,6 +10,7 @@ var Rank = require('./rank.js');
var Auth = require('./auth.js');
var Channel = require('./channel.js').Channel;
var Server = require('./server.js');
+var Database = require('./database.js');
// Represents a client connected via socket.io
var User = function(socket, ip) {
@@ -133,8 +134,65 @@ User.prototype.initCallbacks = function() {
}
}.bind(this));
+ this.socket.on('adm', function(data) {
+ if(Rank.hasPermission(this, "acp")) {
+ this.handleAdm(data);
+ }
+ }.bind(this));
}
+// Handle administration
+User.prototype.handleAdm = function(data) {
+ if(data.cmd == "listloadedchannels") {
+ var chans = [];
+ for(var chan in Server.channels) {
+ var users = [];
+ for(var i = 0; i < Server.channels[chan].users.length; i++) {
+ users.push(Server.channels[chan].users[i].name);
+ }
+ chans.push({
+ chan: chan,
+ users: users
+ });
+ }
+ this.socket.emit('adm', {
+ cmd: "listloadedchannels",
+ chans: chans
+ });
+ }
+ else if(data.cmd == "listchannels") {
+ this.socket.emit('adm', {
+ cmd: "listchannels",
+ chans: Database.listChannels()
+ });
+ }
+ else if(data.cmd == "listusers") {
+ var users = [];
+ var dbusers = Database.listUsers();
+ if(!dbusers)
+ return;
+ for(var i = 0; i < dbusers.length; i++) {
+ users[i] = {
+ name: dbusers[i].uname,
+ rank: dbusers[i].global_rank
+ };
+ }
+ this.socket.emit('adm', {
+ cmd: "listusers",
+ users: users
+ });
+ }
+ else if(data.cmd == "listchannelranks") {
+ if(data.chan == undefined)
+ return;
+ this.socket.emit('adm', {
+ cmd: "listchannelranks",
+ ranks: Database.listChannelRanks(data.chan)
+ });
+ }
+
+};
+
// Attempt to login
User.prototype.login = function(name, sha256) {
// No password => try guest login
diff --git a/www/acp.html b/www/acp.html
new file mode 100644
index 00000000..45a36b25
--- /dev/null
+++ b/www/acp.html
@@ -0,0 +1,90 @@
+
+
+
+
+ Sync
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Sync
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Channel List
+
+
+
+ id |
+ Channel Name |
+
+
+
+
+
+
Channel:
+
+
+
+
+ Username |
+ Rank |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/www/assets/js/acp.js b/www/assets/js/acp.js
new file mode 100644
index 00000000..484166b0
--- /dev/null
+++ b/www/assets/js/acp.js
@@ -0,0 +1,183 @@
+/**
+ * Copyright 2013 Calvin 'calzoneman' Montgomery
+ *
+ * Licensed under Creative Commons Attribution-NonCommercial 3.0
+ * See http://creativecommons.org/licenses/by-nc/3.0/
+ *
+ */
+
+var RANK = 0;
+var uname = readCookie('sync_uname');
+var pw = readCookie('sync_pw');
+var manageChannel = false;
+
+var Rank = {
+ Guest: 0,
+ Member: 1,
+ Moderator: 2,
+ Owner: 3,
+ Siteadmin: 255
+};
+
+var socket = io.connect(IO_URL);
+initCallbacks();
+
+function initCallbacks() {
+
+ socket.on('adm', function(data) {
+ console.log(data);
+ if(data.cmd == "listchannels")
+ handleChannelList(data);
+ if(data.cmd == "listchannelranks")
+ handleChannelRanks(data);
+ });
+
+ socket.on('login', function(data) {
+ if(data.success && $('#password').val()) {
+ createCookie('sync_uname', uname, 1);
+ createCookie('sync_pw', pw, 1);
+ }
+ if(data.success) {
+ $('#loggedin').css('display', '');
+ $('#logoutform').css('display', '');
+ $('#loginform').css('display', 'none');
+ }
+ socket.emit('adm', {
+ cmd: "listloadedchannels"
+ });
+ socket.emit('adm', {
+ cmd: "listchannels"
+ });
+ socket.emit('adm', {
+ cmd: "listusers"
+ });
+ });
+}
+
+function handleChannelList(data) {
+ if($('#chanlist').children.length > 1)
+ $($('#chanlist').children()[1]).remove();
+ for(var i = 0; i < data.chans.length; i++) {
+ var row = $('
').appendTo($('#chanlist'));
+ var id = $(' | ').appendTo(row).text(data.chans[i].id);
+ var name = $(' | ').appendTo(row).text(data.chans[i].name);
+ var manage = $('').addClass("btn pull-right").appendTo(name)
+ .text('Manage Ranks');
+ var cname = data.chans[i].name;
+ manage.click(function() {
+ manageChannelRanks(this);
+ }.bind(cname));
+ }
+}
+
+function manageChannelRanks(name) {
+ manageChannel = name;
+ $('#channelh3').text('Channel: ' + name);
+ socket.emit('adm', {
+ cmd: "listchannelranks",
+ chan: name
+ });
+}
+
+function handleChannelRanks(data) {
+ if($('#chanranks').children.length > 1)
+ $($('#chanranks').children()[1]).remove();
+ for(var i = 0; i < data.ranks.length; i++) {
+ var row = $('
').appendTo($('#chanranks'));
+ var id = $(' | ').appendTo(row).text(data.ranks[i].name);
+ var rank = $(' | ').appendTo(row);
+ var rtxt = $('').appendTo(rank).text(data.ranks[i].rank);
+ var edit = $('').addClass("btn pull-right").appendTo(rank)
+ .text('Edit');
+
+ edit.click(function() {
+ var txt = rtxt.text();
+ rtxt.text('');
+ var textbox = $('').attr('type', 'text').attr('value', txt)
+ .insertBefore(edit, rank);
+ textbox.focus();
+ textbox.blur(function() {
+ rtxt.text(textbox.val());
+ socket.emit('adm', {
+ cmd: "updatechanrank",
+ chan: manageChannel,
+ user: id.text(),
+ rank: parseInt(textbox.val())
+ });
+ textbox.remove();
+ });
+ });
+ }
+}
+
+var params = {};
+if(window.location.search) {
+ var parameters = window.location.search.substring(1).split('&');
+ for(var i = 0; i < parameters.length; i++) {
+ var s = parameters[i].split('=');
+ if(s.length != 2)
+ continue;
+ params[s[0]] = s[1];
+ }
+}
+
+if(uname != null && pw != null && pw != "false") {
+ socket.emit('login', {
+ name: uname,
+ sha256: pw
+ });
+}
+
+function loginClick() {
+ uname = $('#username').val();
+ if($('#password').val() == "")
+ pw = "";
+ else
+ pw = SHA256($('#password').val());
+ socket.emit('login', {
+ name: uname,
+ sha256: pw
+ });
+};
+
+$('#login').click(loginClick);
+$('#username').keydown(function(ev) {
+ if(ev.key == 13)
+ loginClick();
+});
+$('#password').keydown(function(ev) {
+ if(ev.key == 13)
+ loginClick();
+});
+
+$('#logout').click(function() {
+ eraseCookie('sync_uname');
+ eraseCookie('sync_pw');
+ document.location.reload(true);
+});
+
+
+function createCookie(name,value,days) {
+ if (days) {
+ var date = new Date();
+ date.setTime(date.getTime()+(days*24*60*60*1000));
+ var expires = "; expires="+date.toGMTString();
+ }
+ else var expires = "";
+ document.cookie = name+"="+value+expires+"; path=/";
+}
+
+function readCookie(name) {
+ var nameEQ = name + "=";
+ var ca = document.cookie.split(';');
+ for(var i=0;i < ca.length;i++) {
+ var c = ca[i];
+ while (c.charAt(0)==' ') c = c.substring(1,c.length);
+ if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
+ }
+ return null;
+}
+
+function eraseCookie(name) {
+ createCookie(name,"",-1);
+}
diff --git a/www/assets/js/client.js b/www/assets/js/client.js
index 86b2d988..085d1206 100644
--- a/www/assets/js/client.js
+++ b/www/assets/js/client.js
@@ -6,7 +6,6 @@
*
*/
-const IO_URL = "http://somewebsite:1337";
const SYNC_THRESHOLD = 2;
var LEADER = false;
var PLAYER = false;
diff --git a/www/assets/js/iourl.js b/www/assets/js/iourl.js
new file mode 100644
index 00000000..94161345
--- /dev/null
+++ b/www/assets/js/iourl.js
@@ -0,0 +1,9 @@
+/**
+ * Copyright 2013 Calvin 'calzoneman' Montgomery
+ *
+ * Licensed under Creative Commons Attribution-NonCommercial 3.0
+ * See http://creativecommons.org/licenses/by-nc/3.0/
+ *
+ */
+
+const IO_URL = "http://localhost:1337";
diff --git a/www/index.html b/www/index.html
index 9dd4d60c..c33844bf 100644
--- a/www/index.html
+++ b/www/index.html
@@ -94,6 +94,7 @@
+