mirror of https://github.com/calzoneman/sync.git
Banlist and recent login history
This commit is contained in:
parent
32af68a68e
commit
449d01180a
184
channel.js
184
channel.js
|
@ -93,7 +93,9 @@ var Channel = function(name) {
|
|||
};
|
||||
this.ipbans = {};
|
||||
this.namebans = {};
|
||||
this.logins = {};
|
||||
this.ip_alias = {};
|
||||
this.name_alias = {};
|
||||
this.login_hist = [];
|
||||
this.logger = new Logger.Logger("chanlogs/" + this.name + ".log");
|
||||
this.i = 0;
|
||||
this.time = new Date().getTime();
|
||||
|
@ -205,10 +207,6 @@ Channel.prototype.loadDump = function() {
|
|||
this.motd = data.motd;
|
||||
this.broadcastMotd();
|
||||
}
|
||||
data.logins = data.logins || {};
|
||||
for(var ip in data.logins) {
|
||||
this.logins[ip] = data.logins[ip];
|
||||
}
|
||||
this.setLock(!(data.openqueue || false));
|
||||
this.chatbuffer = data.chatbuffer || [];
|
||||
for(var i = 0; i < this.chatbuffer.length; i++) {
|
||||
|
@ -242,7 +240,6 @@ Channel.prototype.saveDump = function() {
|
|||
permissions: this.permissions,
|
||||
filters: filts,
|
||||
motd: this.motd,
|
||||
logins: this.logins,
|
||||
openqueue: this.openqueue,
|
||||
chatbuffer: this.chatbuffer,
|
||||
css: this.css,
|
||||
|
@ -323,11 +320,9 @@ Channel.prototype.saveRank = function(user) {
|
|||
|
||||
Channel.prototype.getIPRank = function(ip) {
|
||||
var names = [];
|
||||
if(this.logins[ip] === undefined || this.logins[ip].length == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
this.logins[ip].forEach(function(name) {
|
||||
if(!(ip in this.ip_alias))
|
||||
this.ip_alias = Database.getAliases(ip);
|
||||
this.ip_alias[ip].forEach(function(name) {
|
||||
names.push(name);
|
||||
});
|
||||
|
||||
|
@ -339,16 +334,6 @@ Channel.prototype.getIPRank = function(ip) {
|
|||
return rank;
|
||||
}
|
||||
|
||||
Channel.prototype.seen = function(ip, name) {
|
||||
name = name.toLowerCase();
|
||||
for(var i = 0; i < this.logins[ip].length; i++) {
|
||||
if(this.logins[ip][i].toLowerCase() == name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Channel.prototype.cacheMedia = function(media) {
|
||||
// Prevent the copy in the playlist from messing with this one
|
||||
media = media.dup();
|
||||
|
@ -362,7 +347,7 @@ Channel.prototype.cacheMedia = function(media) {
|
|||
return false;
|
||||
}
|
||||
|
||||
Channel.prototype.banName = function(actor, name) {
|
||||
Channel.prototype.tryNameBan = function(actor, name) {
|
||||
if(!this.hasPermission(actor, "ban")) {
|
||||
return false;
|
||||
}
|
||||
|
@ -370,11 +355,6 @@ Channel.prototype.banName = function(actor, name) {
|
|||
name = name.toLowerCase();
|
||||
|
||||
var rank = this.getRank(name);
|
||||
if(rank < 1) {
|
||||
actor.socket.emit("errorMsg", {msg: "You can't ban guest names. Use a kick or IP ban."});
|
||||
return false;
|
||||
}
|
||||
|
||||
if(rank >= actor.rank) {
|
||||
actor.socket.emit("errorMsg", {msg: "You don't have permission to ban this person."});
|
||||
return false;
|
||||
|
@ -409,47 +389,47 @@ Channel.prototype.unbanName = function(actor, name) {
|
|||
return Database.channelUnbanName(this.name, name);
|
||||
}
|
||||
|
||||
Channel.prototype.tryIPBan = function(actor, data) {
|
||||
Channel.prototype.tryIPBan = function(actor, name, range) {
|
||||
if(!this.hasPermission(actor, "ban")) {
|
||||
return false;
|
||||
}
|
||||
if(typeof data.id != "string") {
|
||||
return false;
|
||||
}
|
||||
if(typeof data.name != "string") {
|
||||
return false;
|
||||
}
|
||||
var ip = this.hideIP(data.id);
|
||||
if(this.getIPRank(ip) >= actor.rank) {
|
||||
actor.socket.emit("errorMsg", {msg: "You don't have permission to ban this IP"});
|
||||
if(typeof name != "string") {
|
||||
return false;
|
||||
}
|
||||
var ips = Database.ipForName(name);
|
||||
var chan = this;
|
||||
ips.forEach(function(ip) {
|
||||
if(chan.getIPRank(ip) >= actor.rank) {
|
||||
actor.socket.emit("errorMsg", {msg: "You don't have permission to ban IP: x.x." + ip.replace(/\d+\.\d+\.(\d+\.\d+)/, "$1")});
|
||||
return false;
|
||||
}
|
||||
|
||||
if(data.range) {
|
||||
ip = ip.replace(/(\d+)\.(\d+)\.(\d+)\.(\d+)/, "$1.$2.$3");
|
||||
for(var ip2 in this.logins) {
|
||||
if(ip2.indexOf(ip) == 0 && this.getIPRank(ip2) >= actor.rank) {
|
||||
actor.socket.emit("errorMsg", {msg: "You don't have permission to ban this IP"});
|
||||
return false;
|
||||
if(range) {
|
||||
ip = ip.replace(/(\d+)\.(\d+)\.(\d+)\.(\d+)/, "$1.$2.$3");
|
||||
for(var ip2 in chan.ip_alias) {
|
||||
if(ip2.indexOf(ip) == 0 && chan.getIPRank(ip2) >= actor.rank) {
|
||||
actor.socket.emit("errorMsg", {msg: "You don't have permission to ban IP: x.x." + ip2.replace(/\d+\.\d+\.(\d+\.\d+)/, "$1")});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.ipbans[ip] = [data.name, actor.name];
|
||||
this.broadcastBanlist();
|
||||
this.logger.log(ip + " (" + data.name + ") was banned by " + actor.name);
|
||||
chan.ipbans[ip] = [name, actor.name];
|
||||
chan.broadcastBanlist();
|
||||
chan.logger.log(ip + " (" + name + ") was banned by " + actor.name);
|
||||
|
||||
for(var i = 0; i < this.users.length; i++) {
|
||||
if(this.users[i].ip.indexOf(ip) == 0) {
|
||||
this.kick(this.users[i], "Your IP is banned!");
|
||||
i--;
|
||||
for(var i = 0; i < chan.users.length; i++) {
|
||||
if(chan.users[i].ip.indexOf(ip) == 0) {
|
||||
chan.kick(chan.users[i], "Your IP is banned!");
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!this.registered)
|
||||
return false;
|
||||
if(!chan.registered)
|
||||
return false;
|
||||
|
||||
// Update database ban table
|
||||
return Database.channelBan(this.name, ip, data.name, actor.name);
|
||||
// Update database ban table
|
||||
return Database.channelBan(chan.name, ip, name, actor.name);
|
||||
});
|
||||
}
|
||||
|
||||
Channel.prototype.banIP = function(actor, receiver) {
|
||||
|
@ -488,8 +468,8 @@ Channel.prototype.unbanIP = function(actor, ip) {
|
|||
}
|
||||
|
||||
Channel.prototype.tryUnban = function(actor, data) {
|
||||
if(data.id) {
|
||||
var ip = this.hideIP(data.id);
|
||||
if(data.ip_hidden) {
|
||||
var ip = this.hideIP(data.ip_hidden);
|
||||
this.unbanIP(actor, ip);
|
||||
}
|
||||
else if(data.name) {
|
||||
|
@ -531,9 +511,6 @@ Channel.prototype.search = function(query, callback) {
|
|||
/* REGION User interaction */
|
||||
|
||||
Channel.prototype.userJoin = function(user) {
|
||||
if(!(user.ip in this.logins)) {
|
||||
this.logins[user.ip] = [];
|
||||
}
|
||||
var parts = user.ip.split(".");
|
||||
var slash24 = parts[0] + "." + parts[1] + "." + parts[2];
|
||||
// GTFO
|
||||
|
@ -653,27 +630,29 @@ Channel.prototype.hideIP = function(ip) {
|
|||
return chars.join("");
|
||||
}
|
||||
|
||||
Channel.prototype.sendLoginHistory = function(user) {
|
||||
if(user.rank < 2)
|
||||
return;
|
||||
|
||||
user.socket.emit("recentLogins", this.login_hist);
|
||||
}
|
||||
|
||||
Channel.prototype.sendRankStuff = function(user) {
|
||||
if(this.hasPermission(user, "ban")) {
|
||||
var ents = [];
|
||||
for(var ip in this.ipbans) {
|
||||
if(this.ipbans[ip] != null) {
|
||||
var name;
|
||||
if(ip in this.logins) {
|
||||
name = this.logins[ip].join(", ");
|
||||
}
|
||||
else {
|
||||
name = this.ipbans[ip][0];
|
||||
}
|
||||
var id = this.hideIP(ip);
|
||||
var name = this.ipbans[ip][0];
|
||||
var ip_hidden = this.hideIP(ip);
|
||||
var disp = ip;
|
||||
if(user.rank < Rank.Siteadmin) {
|
||||
disp = "(Hidden)";
|
||||
disp = "x.x." + ip.replace(/\d+\.\d+\.(\d+\.\d+)/, "$1");
|
||||
}
|
||||
ents.push({
|
||||
ip: disp,
|
||||
id: id,
|
||||
ip_displayed: disp,
|
||||
ip_hidden: ip_hidden,
|
||||
name: name,
|
||||
aliases: this.ip_alias[ip] || [],
|
||||
banner: this.ipbans[ip][1]
|
||||
});
|
||||
}
|
||||
|
@ -681,17 +660,20 @@ Channel.prototype.sendRankStuff = function(user) {
|
|||
for(var name in this.namebans) {
|
||||
if(this.namebans[name] != null) {
|
||||
ents.push({
|
||||
ip: "*",
|
||||
ip_displayed: "*",
|
||||
ip_hidden: false,
|
||||
name: name,
|
||||
aliases: this.name_alias[name] || [],
|
||||
banner: this.namebans[name]
|
||||
});
|
||||
}
|
||||
}
|
||||
user.socket.emit("banlist", {entries: ents});
|
||||
user.socket.emit("banlist", ents);
|
||||
}
|
||||
// TODO get rid of this
|
||||
if(Rank.hasPermission(user, "seenlogins")) {
|
||||
var ents = [];
|
||||
for(var ip in this.logins) {
|
||||
for(var ip in this.ip_alias) {
|
||||
var disp = ip;
|
||||
if(user.rank < Rank.Siteadmin) {
|
||||
disp = "(Hidden)";
|
||||
|
@ -702,7 +684,7 @@ Channel.prototype.sendRankStuff = function(user) {
|
|||
ents.push({
|
||||
ip: disp,
|
||||
id: this.hideIP(ip),
|
||||
names: this.logins[ip],
|
||||
names: this.ip_alias[ip],
|
||||
banned: banned
|
||||
});
|
||||
}
|
||||
|
@ -797,9 +779,21 @@ Channel.prototype.broadcastUsercount = function() {
|
|||
}
|
||||
|
||||
Channel.prototype.broadcastNewUser = function(user) {
|
||||
if(!this.seen(user.ip, user.name)) {
|
||||
this.logins[user.ip].push(user.name);
|
||||
}
|
||||
var aliases = Database.getAliases(user.ip);
|
||||
var chan = this;
|
||||
this.ip_alias[user.ip] = aliases;
|
||||
aliases.forEach(function(alias) {
|
||||
chan.name_alias[alias] = aliases;
|
||||
});
|
||||
|
||||
this.login_hist.unshift({
|
||||
name: user.name,
|
||||
aliases: this.ip_alias[user.ip],
|
||||
time: Date.now()
|
||||
});
|
||||
if(this.login_hist.length > 20)
|
||||
this.login_hist.pop();
|
||||
|
||||
if(user.name.toLowerCase() in this.namebans &&
|
||||
this.namebans[user.name.toLowerCase()] != null) {
|
||||
this.kick(user, "You're banned!");
|
||||
|
@ -850,24 +844,20 @@ Channel.prototype.broadcastBanlist = function() {
|
|||
var adminents = [];
|
||||
for(var ip in this.ipbans) {
|
||||
if(this.ipbans[ip] != null) {
|
||||
var name;
|
||||
if(ip in this.logins) {
|
||||
name = this.logins[ip].join(", ");
|
||||
}
|
||||
else {
|
||||
name = this.ipbans[ip][0];
|
||||
}
|
||||
var id = this.hideIP(ip);
|
||||
var name = this.ipbans[ip][0];
|
||||
var ip_hidden = this.hideIP(ip);
|
||||
ents.push({
|
||||
ip: "(Hidden)",
|
||||
id: id,
|
||||
ip_displayed: "x.x." + ip.replace(/\d+\.\d+\.(\d+\.\d+)/, "$1"),
|
||||
ip_hidden: ip_hidden,
|
||||
name: name,
|
||||
aliases: this.ip_alias[ip] || [],
|
||||
banner: this.ipbans[ip][1]
|
||||
});
|
||||
adminents.push({
|
||||
ip: ip,
|
||||
id: id,
|
||||
ip_displayed: ip,
|
||||
ip_hidden: ip_hidden,
|
||||
name: name,
|
||||
aliases: this.ip_alias[ip] || [],
|
||||
banner: this.ipbans[ip][1]
|
||||
});
|
||||
}
|
||||
|
@ -875,13 +865,17 @@ Channel.prototype.broadcastBanlist = function() {
|
|||
for(var name in this.namebans) {
|
||||
if(this.namebans[name] != null) {
|
||||
ents.push({
|
||||
ip: "*",
|
||||
ip_displayed: "*",
|
||||
ip_hidden: false,
|
||||
name: name,
|
||||
aliases: this.name_alias[name] || [],
|
||||
banner: this.namebans[name]
|
||||
});
|
||||
adminents.push({
|
||||
ip: "*",
|
||||
ip_displayed: "*",
|
||||
ip_hidden: false,
|
||||
name: name,
|
||||
aliases: this.name_alias[name] || [],
|
||||
banner: this.namebans[name]
|
||||
});
|
||||
}
|
||||
|
@ -889,10 +883,10 @@ Channel.prototype.broadcastBanlist = function() {
|
|||
for(var i = 0; i < this.users.length; i++) {
|
||||
if(this.hasPermission(this.users[i], "ban")) {
|
||||
if(this.users[i].rank >= Rank.Siteadmin) {
|
||||
this.users[i].socket.emit("banlist", {entries: adminents});
|
||||
this.users[i].socket.emit("banlist", adminents);
|
||||
}
|
||||
else {
|
||||
this.users[i].socket.emit("banlist", {entries: ents});
|
||||
this.users[i].socket.emit("banlist", ents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ function handle(chan, user, msg, data) {
|
|||
handleBan(chan, user, msg.substring(5).split(" "));
|
||||
}
|
||||
else if(msg.indexOf("/ipban ") == 0) {
|
||||
handleIPBan(chan, user, msg.substring(5).split(" "));
|
||||
handleIPBan(chan, user, msg.substring(7).split(" "));
|
||||
}
|
||||
else if(msg.indexOf("/unban ") == 0) {
|
||||
handleUnban(chan, user, msg.substring(7).split(" "));
|
||||
|
@ -77,30 +77,13 @@ function handleKick(chan, user, args) {
|
|||
}
|
||||
|
||||
function handleIPBan(chan, user, args) {
|
||||
if(chan.hasPermission(user, "ban") && args.length > 0) {
|
||||
args[0] = args[0].toLowerCase();
|
||||
var kickee;
|
||||
for(var i = 0; i < chan.users.length; i++) {
|
||||
if(chan.users[i].name.toLowerCase() == args[0]) {
|
||||
kickee = chan.users[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(kickee && kickee.rank < user.rank) {
|
||||
chan.logger.log("*** " + user.name + " banned " + args[0]);
|
||||
args[0] = "";
|
||||
var reason = args.join(" ");
|
||||
chan.kick(kickee, "(banned) " + reason);
|
||||
chan.banIP(user, kickee);
|
||||
}
|
||||
}
|
||||
chan.tryIPBan(user, args[0], args[1]);
|
||||
// Ban the name too for good measure
|
||||
chan.tryNameBan(user, args[0]);
|
||||
}
|
||||
|
||||
function handleBan(chan, user, args) {
|
||||
if(chan.hasPermission(user, "ban") && args.length > 0) {
|
||||
args[0] = args[0].toLowerCase();
|
||||
chan.banName(user, args[0]);
|
||||
}
|
||||
chan.tryNameBan(user, args[0]);
|
||||
}
|
||||
|
||||
function handleUnban(chan, user, args) {
|
||||
|
|
54
database.js
54
database.js
|
@ -918,7 +918,6 @@ function recordVisit(ip, name) {
|
|||
|
||||
var results = db.querySync(query);
|
||||
if(!results) {
|
||||
console.log(results);
|
||||
Logger.errlog.log("! Failed to record visit");
|
||||
}
|
||||
|
||||
|
@ -931,11 +930,60 @@ function recordVisit(ip, name) {
|
|||
");"].join(""),
|
||||
[ip]
|
||||
));
|
||||
console.log(results);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
function getAliases(ip) {
|
||||
var db = getConnection();
|
||||
if(!db) {
|
||||
return [];
|
||||
}
|
||||
|
||||
var query = createQuery(
|
||||
"SELECT name FROM aliases WHERE ip=?",
|
||||
[ip]
|
||||
);
|
||||
|
||||
var results = db.querySync(query);
|
||||
if(!results) {
|
||||
Logger.errlog.log("! Failed to retrieve aliases");
|
||||
return [];
|
||||
}
|
||||
|
||||
var names = [];
|
||||
results.fetchAllSync().forEach(function(row) {
|
||||
names.push(row.name);
|
||||
});
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
function ipForName(name) {
|
||||
var db = getConnection();
|
||||
if(!db) {
|
||||
return [];
|
||||
}
|
||||
|
||||
var query = createQuery(
|
||||
"SELECT ip FROM aliases WHERE name=?",
|
||||
[name]
|
||||
);
|
||||
|
||||
var results = db.querySync(query);
|
||||
if(!results) {
|
||||
Logger.errlog.log("! Failed to retrieve IP for name");
|
||||
return [];
|
||||
}
|
||||
|
||||
var ips = [];
|
||||
results.fetchAllSync().forEach(function(row) {
|
||||
ips.push(row.ip);
|
||||
});
|
||||
|
||||
return ips;
|
||||
}
|
||||
|
||||
exports.setup = setup;
|
||||
exports.getConnection = getConnection;
|
||||
exports.createQuery = createQuery;
|
||||
|
@ -966,3 +1014,5 @@ exports.loadUserPlaylist = loadUserPlaylist;
|
|||
exports.saveUserPlaylist = saveUserPlaylist;
|
||||
exports.deleteUserPlaylist = deleteUserPlaylist;
|
||||
exports.recordVisit = recordVisit;
|
||||
exports.getAliases = getAliases;
|
||||
exports.ipForName = ipForName;
|
||||
|
|
6
user.js
6
user.js
|
@ -379,6 +379,12 @@ User.prototype.initCallbacks = function() {
|
|||
}
|
||||
}.bind(this));
|
||||
|
||||
this.socket.on("requestLoginHistory", function() {
|
||||
if(this.channel != null) {
|
||||
this.channel.sendLoginHistory(this);
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
this.socket.on("requestAcl", function() {
|
||||
if(this.channel != null) {
|
||||
this.channel.sendACL(this);
|
||||
|
|
|
@ -250,26 +250,60 @@ Callbacks = {
|
|||
|
||||
banlist: function(entries) {
|
||||
var tbl = $("#banlist table");
|
||||
// dumb hack because of jquery UI
|
||||
// sortable turns tables and lists into a mess of race conditions
|
||||
if(!tbl.hasClass("table")) {
|
||||
setTimeout(function() {
|
||||
Callbacks.banlist(entries);
|
||||
}, 100);
|
||||
return;
|
||||
}
|
||||
if(tbl.children().length > 1) {
|
||||
$(tbl.children()[1]).remove();
|
||||
}
|
||||
for(var i = 0; i < entries.length; i++) {
|
||||
var tr = $("<tr/>").appendTo(tbl);
|
||||
var tr = document.createElement("tr");
|
||||
var remove = $("<button/>").addClass("btn btn-mini btn-danger")
|
||||
.appendTo($("<td/>").appendTo(tr));
|
||||
$("<i/>").addClass("icon-remove-circle").appendTo(remove);
|
||||
var ip = $("<td/>").text(entries[i].ip).appendTo(tr);
|
||||
var ip = $("<td/>").text(entries[i].ip_displayed).appendTo(tr);
|
||||
var name = $("<td/>").text(entries[i].name).appendTo(tr);
|
||||
var aliases = $("<td/>").text(entries[i].aliases).appendTo(tr);
|
||||
var aliases = $("<td/>").text(entries[i].aliases.join(", ")).appendTo(tr);
|
||||
var banner = $("<td/>").text(entries[i].banner).appendTo(tr);
|
||||
|
||||
var callback = (function(id, name) { return function() {
|
||||
var callback = (function(ip, name) { return function() {
|
||||
socket.emit("unban", {
|
||||
id: id,
|
||||
ip: ip,
|
||||
name: name
|
||||
});
|
||||
} })(entries[i].id, entries[i].name);
|
||||
} })(entries[i].ip_hidden, entries[i].name);
|
||||
remove.click(callback);
|
||||
|
||||
$(tr).appendTo(tbl);
|
||||
}
|
||||
},
|
||||
|
||||
recentLogins: function(entries) {
|
||||
var tbl = $("#loginhistory table");
|
||||
// dumb hack because of jquery UI
|
||||
// sortable turns tables and lists into a mess of race conditions
|
||||
if(!tbl.hasClass("table")) {
|
||||
setTimeout(function() {
|
||||
Callbacks.recentLogins(entries);
|
||||
}, 100);
|
||||
return;
|
||||
}
|
||||
if(tbl.children().length > 1) {
|
||||
$(tbl.children()[1]).remove();
|
||||
}
|
||||
for(var i = 0; i < entries.length; i++) {
|
||||
var tr = document.createElement("tr");
|
||||
var name = $("<td/>").text(entries[i].name).appendTo(tr);
|
||||
var aliases = $("<td/>").text(entries[i].aliases.join(", ")).appendTo(tr);
|
||||
var time = new Date(entries[i].time).toTimeString();
|
||||
$("<td/>").text(time).appendTo(tr);
|
||||
|
||||
$(tr).appendTo(tbl);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -20,6 +20,11 @@
|
|||
clickHandler("#show_filteredit", "#filteredit");
|
||||
clickHandler("#show_cssedit", "#cssedit");
|
||||
clickHandler("#show_jsedit", "#jsedit");
|
||||
clickHandler("#show_banlist", "#banlist");
|
||||
clickHandler("#show_loginhistory", "#loginhistory");
|
||||
$("#show_loginhistory").click(function() {
|
||||
socket.emit("requestLoginHistory");
|
||||
});
|
||||
|
||||
genPermissionsEditor();
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<li id="cssedit_tab"><a href="javascript:void(0);" id="show_cssedit">CSS Editor</a></li>
|
||||
<li id="jsedit_tab"><a href="javascript:void(0);" id="show_jsedit">JS Editor</a></li>
|
||||
<li id="banlist_tab"><a href="javascript:void(0);" id="show_banlist">Ban List</a></li>
|
||||
<li id="recentconn_tab"><a href="javascript:void(0);" id="show_recentconn">Recent Connections</a></li>
|
||||
<li id="loginhistory_tab"><a href="javascript:void(0);" id="show_loginhistory">Recent Connections</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<hr>
|
||||
|
@ -102,3 +102,27 @@ Filters Here
|
|||
<textarea rows="10" id="jstext"></textarea>
|
||||
<button class="btn btn-primary" id="save_js">Save</button>
|
||||
</div>
|
||||
<div id="banlist" class="span12">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Unban</th>
|
||||
<th>IP</th>
|
||||
<th>Name</th>
|
||||
<th>Aliases</th>
|
||||
<th>Banned By</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
<div id="loginhistory" class="span12">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Aliases</th>
|
||||
<th>Time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue