Work on banned channels feature
This commit is contained in:
parent
7921f41174
commit
8338fe2f25
|
@ -94,7 +94,10 @@ function Channel(name) {
|
||||||
}, USERCOUNT_THROTTLE);
|
}, USERCOUNT_THROTTLE);
|
||||||
const self = this;
|
const self = this;
|
||||||
db.channels.load(this, function (err) {
|
db.channels.load(this, function (err) {
|
||||||
if (err && err !== "Channel is not registered") {
|
if (err && err.code === 'EBANNED') {
|
||||||
|
self.emit("loadFail", err.message);
|
||||||
|
self.setFlag(Flags.C_ERROR);
|
||||||
|
} else if (err && err !== "Channel is not registered") {
|
||||||
self.emit("loadFail", "Failed to load channel data from the database. Please try again later.");
|
self.emit("loadFail", "Failed to load channel data from the database. Please try again later.");
|
||||||
self.setFlag(Flags.C_ERROR);
|
self.setFlag(Flags.C_ERROR);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -209,7 +209,9 @@ module.exports = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
db.query("SELECT * FROM `channels` WHERE owner=?", [owner],
|
db.query("SELECT c.*, bc.external_reason as banReason " +
|
||||||
|
"FROM channels c LEFT OUTER JOIN banned_channels bc " +
|
||||||
|
"ON bc.channel_name = c.name WHERE c.owner=?", [owner],
|
||||||
function (err, res) {
|
function (err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
callback(err, []);
|
callback(err, []);
|
||||||
|
@ -245,13 +247,28 @@ module.exports = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
db.query("SELECT * FROM `channels` WHERE name=?", chan.name, function (err, res) {
|
db.query("SELECT c.*, bc.external_reason as banReason " +
|
||||||
|
"FROM channels c LEFT OUTER JOIN banned_channels bc " +
|
||||||
|
"ON bc.channel_name = c.name WHERE c.name=? " +
|
||||||
|
"UNION " +
|
||||||
|
"SELECT c.*, bc.external_reason as banReason " +
|
||||||
|
"FROM channels c RIGHT OUTER JOIN banned_channels bc " +
|
||||||
|
"ON bc.channel_name = c.name WHERE bc.channel_name=? ",
|
||||||
|
[chan.name, chan.name],
|
||||||
|
function (err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
callback(err, null);
|
callback(err, null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res.length === 0) {
|
if (res.length > 0 && res[0].banReason !== null) {
|
||||||
|
let banError = new Error(`Channel is banned: ${res[0].banReason}`);
|
||||||
|
banError.code = 'EBANNED';
|
||||||
|
callback(banError, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.length === 0 || res[0].id === null) {
|
||||||
callback("Channel is not registered", null);
|
callback("Channel is not registered", null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -704,5 +721,30 @@ module.exports = {
|
||||||
LOGGER.error(`Failed to update owner_last_seen column for channel ID ${channelId}: ${error}`);
|
LOGGER.error(`Failed to update owner_last_seen column for channel ID ${channelId}: ${error}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getBannedChannel: async function getBannedChannel(name) {
|
||||||
|
if (!valid(name)) {
|
||||||
|
throw new Error("Invalid channel name");
|
||||||
|
}
|
||||||
|
|
||||||
|
return await db.getDB().runTransaction(async tx => {
|
||||||
|
let rows = await tx.table('banned_channels')
|
||||||
|
.where({ channel_name: name })
|
||||||
|
.select();
|
||||||
|
|
||||||
|
if (rows.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
channelName: rows[0].channel_name,
|
||||||
|
externalReason: rows[0].external_reason,
|
||||||
|
internalReason: rows[0].internal_reason,
|
||||||
|
bannedBy: rows[0].banned_by,
|
||||||
|
createdAt: rows[0].created_at,
|
||||||
|
updatedAt: rows[0].updated_at
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -156,4 +156,15 @@ export async function initTables() {
|
||||||
t.primary(['type', 'id']);
|
t.primary(['type', 'id']);
|
||||||
t.index('updated_at');
|
t.index('updated_at');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await ensureTable('banned_channels', t => {
|
||||||
|
t.charset('utf8mb4');
|
||||||
|
t.string('channel_name', 30)
|
||||||
|
.notNullable()
|
||||||
|
.unique();
|
||||||
|
t.text('external_reason').notNullable();
|
||||||
|
t.text('internal_reason').notNullable();
|
||||||
|
t.string('banned_by', 20).notNullable();
|
||||||
|
t.timestamps(/* useTimestamps */ true, /* defaultToNow */ true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,7 @@ var Server = function () {
|
||||||
const globalMessageBus = this.initModule.getGlobalMessageBus();
|
const globalMessageBus = this.initModule.getGlobalMessageBus();
|
||||||
globalMessageBus.on('UserProfileChanged', this.handleUserProfileChange.bind(this));
|
globalMessageBus.on('UserProfileChanged', this.handleUserProfileChange.bind(this));
|
||||||
globalMessageBus.on('ChannelDeleted', this.handleChannelDelete.bind(this));
|
globalMessageBus.on('ChannelDeleted', this.handleChannelDelete.bind(this));
|
||||||
|
globalMessageBus.on('ChannelBanned', this.handleChannelBanned.bind(this));
|
||||||
globalMessageBus.on('ChannelRegistered', this.handleChannelRegister.bind(this));
|
globalMessageBus.on('ChannelRegistered', this.handleChannelRegister.bind(this));
|
||||||
|
|
||||||
// database init ------------------------------------------------------
|
// database init ------------------------------------------------------
|
||||||
|
@ -549,6 +550,34 @@ Server.prototype.handleChannelDelete = function (event) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Server.prototype.handleChannelBanned = function (event) {
|
||||||
|
try {
|
||||||
|
const lname = event.channel.toLowerCase();
|
||||||
|
const reason = event.externalReason;
|
||||||
|
|
||||||
|
this.channels.forEach(channel => {
|
||||||
|
if (channel.dead) return;
|
||||||
|
|
||||||
|
if (channel.uniqueName === lname) {
|
||||||
|
channel.clearFlag(Flags.C_REGISTERED);
|
||||||
|
|
||||||
|
const users = Array.prototype.slice.call(channel.users);
|
||||||
|
users.forEach(u => {
|
||||||
|
u.kick(`Channel was banned: ${reason}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!channel.dead && !channel.dying) {
|
||||||
|
channel.emit('empty');
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGGER.info('Processed banned channel %s', lname);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
LOGGER.error('handleChannelBanned failed: %s', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Server.prototype.handleChannelRegister = function (event) {
|
Server.prototype.handleChannelRegister = function (event) {
|
||||||
try {
|
try {
|
||||||
const lname = event.channel.toLowerCase();
|
const lname = event.channel.toLowerCase();
|
||||||
|
|
|
@ -102,10 +102,6 @@ User.prototype.handleJoinChannel = function handleJoinChannel(data) {
|
||||||
|
|
||||||
if (!chan.is(Flags.C_READY)) {
|
if (!chan.is(Flags.C_READY)) {
|
||||||
chan.once("loadFail", reason => {
|
chan.once("loadFail", reason => {
|
||||||
this.socket.emit("errorMsg", {
|
|
||||||
msg: reason,
|
|
||||||
alert: true
|
|
||||||
});
|
|
||||||
this.kick(`Channel could not be loaded: ${reason}`);
|
this.kick(`Channel could not be loaded: ${reason}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -265,6 +265,8 @@ async function handleNewChannel(req, res) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let banInfo = await db.channels.getBannedChannel(name);
|
||||||
|
|
||||||
db.channels.listUserChannels(user.name, function (err, channels) {
|
db.channels.listUserChannels(user.name, function (err, channels) {
|
||||||
if (err) {
|
if (err) {
|
||||||
sendPug(res, "account-channels", {
|
sendPug(res, "account-channels", {
|
||||||
|
@ -274,6 +276,14 @@ async function handleNewChannel(req, res) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (banInfo !== null) {
|
||||||
|
sendPug(res, "account-channels", {
|
||||||
|
channels: channels,
|
||||||
|
newChannelError: `Cannot register "${name}": this channel is banned.`
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (name.match(Config.get("reserved-names.channels"))) {
|
if (name.match(Config.get("reserved-names.channels"))) {
|
||||||
sendPug(res, "account-channels", {
|
sendPug(res, "account-channels", {
|
||||||
channels: channels,
|
channels: channels,
|
||||||
|
|
|
@ -4,13 +4,22 @@ import { sendPug } from '../pug';
|
||||||
import * as HTTPStatus from '../httpstatus';
|
import * as HTTPStatus from '../httpstatus';
|
||||||
import { HTTPError } from '../../errors';
|
import { HTTPError } from '../../errors';
|
||||||
|
|
||||||
export default function initialize(app, ioConfig, chanPath) {
|
export default function initialize(app, ioConfig, chanPath, getBannedChannel) {
|
||||||
app.get(`/${chanPath}/:channel`, (req, res) => {
|
app.get(`/${chanPath}/:channel`, async (req, res) => {
|
||||||
if (!req.params.channel || !CyTubeUtil.isValidChannelName(req.params.channel)) {
|
if (!req.params.channel || !CyTubeUtil.isValidChannelName(req.params.channel)) {
|
||||||
throw new HTTPError(`"${sanitizeText(req.params.channel)}" is not a valid ` +
|
throw new HTTPError(`"${sanitizeText(req.params.channel)}" is not a valid ` +
|
||||||
'channel name.', { status: HTTPStatus.NOT_FOUND });
|
'channel name.', { status: HTTPStatus.NOT_FOUND });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: add a cache
|
||||||
|
let banInfo = await getBannedChannel(req.params.channel);
|
||||||
|
if (banInfo !== null) {
|
||||||
|
sendPug(res, 'banned_channel', {
|
||||||
|
externalReason: banInfo.externalReason
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const endpoints = ioConfig.getSocketEndpoints();
|
const endpoints = ioConfig.getSocketEndpoints();
|
||||||
if (endpoints.length === 0) {
|
if (endpoints.length === 0) {
|
||||||
throw new HTTPError('No socket.io endpoints configured');
|
throw new HTTPError('No socket.io endpoints configured');
|
||||||
|
|
|
@ -194,7 +194,13 @@ module.exports = {
|
||||||
LOGGER.info('Enabled express-minify for CSS and JS');
|
LOGGER.info('Enabled express-minify for CSS and JS');
|
||||||
}
|
}
|
||||||
|
|
||||||
require('./routes/channel')(app, ioConfig, chanPath);
|
require('./routes/channel')(
|
||||||
|
app,
|
||||||
|
ioConfig,
|
||||||
|
chanPath,
|
||||||
|
async name => null
|
||||||
|
/*require('../database/channels').getBannedChannel*/
|
||||||
|
);
|
||||||
require('./routes/index')(app, channelIndex, webConfig.getMaxIndexEntries());
|
require('./routes/index')(app, channelIndex, webConfig.getMaxIndexEntries());
|
||||||
require('./routes/socketconfig')(app, clusterClient);
|
require('./routes/socketconfig')(app, clusterClient);
|
||||||
require('./routes/contact')(app, webConfig);
|
require('./routes/contact')(app, webConfig);
|
||||||
|
|
|
@ -24,6 +24,7 @@ block content
|
||||||
tbody
|
tbody
|
||||||
for c in channels
|
for c in channels
|
||||||
tr
|
tr
|
||||||
|
// TODO: seems like it should be a <td>, lol
|
||||||
th
|
th
|
||||||
form.form-inline.pull-right(action="/account/channels", method="post", onsubmit="return confirm('Are you sure you want to delete " +c.name+ "? This cannot be undone');")
|
form.form-inline.pull-right(action="/account/channels", method="post", onsubmit="return confirm('Are you sure you want to delete " +c.name+ "? This cannot be undone');")
|
||||||
input(type="hidden", name="_csrf", value=csrfToken)
|
input(type="hidden", name="_csrf", value=csrfToken)
|
||||||
|
@ -32,6 +33,9 @@ block content
|
||||||
button.btn.btn-xs.btn-danger(type="submit") Delete
|
button.btn.btn-xs.btn-danger(type="submit") Delete
|
||||||
span.glyphicon.glyphicon-trash
|
span.glyphicon.glyphicon-trash
|
||||||
a(href=`/${channelPath}/${c.name}`, style="margin-left: 5px")= c.name
|
a(href=`/${channelPath}/${c.name}`, style="margin-left: 5px")= c.name
|
||||||
|
if c.banReason != null
|
||||||
|
|
|
||||||
|
span.label.label-danger Banned
|
||||||
.col-lg-6.col-md-6
|
.col-lg-6.col-md-6
|
||||||
h3 Register a new channel
|
h3 Register a new channel
|
||||||
if newChannelError
|
if newChannelError
|
||||||
|
|
9
templates/banned_channel.pug
Normal file
9
templates/banned_channel.pug
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
extends layout.pug
|
||||||
|
|
||||||
|
block content
|
||||||
|
.col-md-12
|
||||||
|
.alert.alert-danger
|
||||||
|
h1 Banned Channel
|
||||||
|
strong This channel is banned:
|
||||||
|
p
|
||||||
|
= externalReason
|
Loading…
Reference in a new issue