Work on banned channels feature
This commit is contained in:
parent
7921f41174
commit
d4727533b1
|
@ -94,7 +94,10 @@ function Channel(name) {
|
|||
}, USERCOUNT_THROTTLE);
|
||||
const self = this;
|
||||
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.setFlag(Flags.C_ERROR);
|
||||
} else {
|
||||
|
|
|
@ -209,7 +209,9 @@ module.exports = {
|
|||
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) {
|
||||
if (err) {
|
||||
callback(err, []);
|
||||
|
@ -245,13 +247,28 @@ module.exports = {
|
|||
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) {
|
||||
callback(err, null);
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
@ -704,5 +721,30 @@ module.exports = {
|
|||
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.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();
|
||||
globalMessageBus.on('UserProfileChanged', this.handleUserProfileChange.bind(this));
|
||||
globalMessageBus.on('ChannelDeleted', this.handleChannelDelete.bind(this));
|
||||
globalMessageBus.on('ChannelBanned', this.handleChannelBanned.bind(this));
|
||||
globalMessageBus.on('ChannelRegistered', this.handleChannelRegister.bind(this));
|
||||
|
||||
// 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) {
|
||||
try {
|
||||
const lname = event.channel.toLowerCase();
|
||||
|
|
|
@ -102,10 +102,6 @@ User.prototype.handleJoinChannel = function handleJoinChannel(data) {
|
|||
|
||||
if (!chan.is(Flags.C_READY)) {
|
||||
chan.once("loadFail", reason => {
|
||||
this.socket.emit("errorMsg", {
|
||||
msg: reason,
|
||||
alert: true
|
||||
});
|
||||
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) {
|
||||
if (err) {
|
||||
sendPug(res, "account-channels", {
|
||||
|
@ -274,6 +276,14 @@ async function handleNewChannel(req, res) {
|
|||
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"))) {
|
||||
sendPug(res, "account-channels", {
|
||||
channels: channels,
|
||||
|
|
|
@ -4,13 +4,22 @@ import { sendPug } from '../pug';
|
|||
import * as HTTPStatus from '../httpstatus';
|
||||
import { HTTPError } from '../../errors';
|
||||
|
||||
export default function initialize(app, ioConfig, chanPath) {
|
||||
app.get(`/${chanPath}/:channel`, (req, res) => {
|
||||
export default function initialize(app, ioConfig, chanPath, getBannedChannel) {
|
||||
app.get(`/${chanPath}/:channel`, async (req, res) => {
|
||||
if (!req.params.channel || !CyTubeUtil.isValidChannelName(req.params.channel)) {
|
||||
throw new HTTPError(`"${sanitizeText(req.params.channel)}" is not a valid ` +
|
||||
'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();
|
||||
if (endpoints.length === 0) {
|
||||
throw new HTTPError('No socket.io endpoints configured');
|
||||
|
|
|
@ -194,7 +194,13 @@ module.exports = {
|
|||
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/socketconfig')(app, clusterClient);
|
||||
require('./routes/contact')(app, webConfig);
|
||||
|
|
|
@ -24,6 +24,7 @@ block content
|
|||
tbody
|
||||
for c in channels
|
||||
tr
|
||||
// TODO: seems like it should be a <td>, lol
|
||||
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');")
|
||||
input(type="hidden", name="_csrf", value=csrfToken)
|
||||
|
@ -32,6 +33,9 @@ block content
|
|||
button.btn.btn-xs.btn-danger(type="submit") Delete
|
||||
span.glyphicon.glyphicon-trash
|
||||
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
|
||||
h3 Register a new channel
|
||||
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