Continue working on banned channels
This commit is contained in:
parent
8338fe2f25
commit
ae5dbf5f48
93
bin/admin.js
Normal file
93
bin/admin.js
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const Config = require('../lib/config');
|
||||||
|
Config.load('config.yaml');
|
||||||
|
|
||||||
|
if (!Config.get('service-socket.enabled')){
|
||||||
|
console.error('The Service Socket is not enabled.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const net = require('net');
|
||||||
|
const path = require('path');
|
||||||
|
const readline = require('node:readline/promises');
|
||||||
|
|
||||||
|
const socketPath = path.resolve(__dirname, '..', Config.get('service-socket.socket'));
|
||||||
|
const rl = readline.createInterface({
|
||||||
|
input: process.stdin,
|
||||||
|
output: process.stdout
|
||||||
|
});
|
||||||
|
|
||||||
|
async function doCommand(params) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const client = net.createConnection(socketPath);
|
||||||
|
|
||||||
|
client.on('connect', () => {
|
||||||
|
client.write(JSON.stringify(params) + '\n');
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('data', data => {
|
||||||
|
client.end();
|
||||||
|
resolve(JSON.parse(data));
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('error', error => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let commands = [
|
||||||
|
{
|
||||||
|
command: 'ban-channel',
|
||||||
|
handler: async args => {
|
||||||
|
if (args.length !== 3) {
|
||||||
|
console.log('Usage: ban-channel <name> <externalReason> <internalReason>');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let [name, externalReason, internalReason] = args;
|
||||||
|
let answer = await rl.question(`Ban ${name} with external reason "${externalReason}" and internal reason "${internalReason}"? `);
|
||||||
|
|
||||||
|
|
||||||
|
if (/^[yY]$/.test(answer)) {
|
||||||
|
let res = await doCommand({
|
||||||
|
command: 'ban-channel',
|
||||||
|
name,
|
||||||
|
externalReason,
|
||||||
|
internalReason
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Status: ${res.status}`);
|
||||||
|
if (res.status === 'error') {
|
||||||
|
console.log('Error:', res.error);
|
||||||
|
process.exit(1);
|
||||||
|
} else {
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('Aborted.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
let found = false;
|
||||||
|
commands.forEach(cmd => {
|
||||||
|
if (cmd.command === process.argv[2]) {
|
||||||
|
found = true;
|
||||||
|
cmd.handler(process.argv.slice(3)).then(() => {
|
||||||
|
process.exit(0);
|
||||||
|
}).catch(error => {
|
||||||
|
console.log('Error in command:', error.stack);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
console.log('Available commands:');
|
||||||
|
commands.forEach(cmd => {
|
||||||
|
console.log(` * ${cmd.command}`);
|
||||||
|
});
|
||||||
|
process.exit(1);
|
||||||
|
}
|
12
src/cli/banned-channels.js
Normal file
12
src/cli/banned-channels.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import Server from '../server';
|
||||||
|
|
||||||
|
export async function handleBanChannel({ name, externalReason, internalReason }) {
|
||||||
|
await Server.getServer().bannedChannelsController.banChannel({
|
||||||
|
name,
|
||||||
|
externalReason,
|
||||||
|
internalReason,
|
||||||
|
bannedBy: '[console]'
|
||||||
|
});
|
||||||
|
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
29
src/controller/banned-channels.js
Normal file
29
src/controller/banned-channels.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
const LOGGER = require('@calzoneman/jsli')('BannedChannelsController');
|
||||||
|
|
||||||
|
export class BannedChannelsController {
|
||||||
|
constructor(dbChannels, globalMessageBus) {
|
||||||
|
this.dbChannels = dbChannels;
|
||||||
|
this.globalMessageBus = globalMessageBus;
|
||||||
|
}
|
||||||
|
|
||||||
|
async banChannel({ name, externalReason, internalReason, bannedBy }) {
|
||||||
|
LOGGER.info(`Banning channel ${name} (banned by ${bannedBy})`);
|
||||||
|
|
||||||
|
let banInfo = await this.dbChannels.getBannedChannel(name);
|
||||||
|
if (banInfo !== null) {
|
||||||
|
LOGGER.warn(`Channel ${name} is already banned, updating ban reason`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.dbChannels.putBannedChannel({
|
||||||
|
name,
|
||||||
|
externalReason,
|
||||||
|
internalReason,
|
||||||
|
bannedBy
|
||||||
|
});
|
||||||
|
|
||||||
|
this.globalMessageBus.emit(
|
||||||
|
'ChannelBanned',
|
||||||
|
{ channel: name, externalReason }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ var db = require("../database");
|
||||||
var valid = require("../utilities").isValidChannelName;
|
var valid = require("../utilities").isValidChannelName;
|
||||||
var Flags = require("../flags");
|
var Flags = require("../flags");
|
||||||
var util = require("../utilities");
|
var util = require("../utilities");
|
||||||
|
// TODO: I think newer knex has native support for this
|
||||||
import { createMySQLDuplicateKeyUpdate } from '../util/on-duplicate-key-update';
|
import { createMySQLDuplicateKeyUpdate } from '../util/on-duplicate-key-update';
|
||||||
import Config from '../config';
|
import Config from '../config';
|
||||||
|
|
||||||
|
@ -746,5 +747,26 @@ module.exports = {
|
||||||
updatedAt: rows[0].updated_at
|
updatedAt: rows[0].updated_at
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
putBannedChannel: async function putBannedChannel({ name, externalReason, internalReason, bannedBy }) {
|
||||||
|
if (!valid(name)) {
|
||||||
|
throw new Error("Invalid channel name");
|
||||||
|
}
|
||||||
|
|
||||||
|
return await db.getDB().runTransaction(async tx => {
|
||||||
|
let insert = tx.table('banned_channels')
|
||||||
|
.insert({
|
||||||
|
channel_name: name,
|
||||||
|
external_reason: externalReason,
|
||||||
|
internal_reason: internalReason,
|
||||||
|
banned_by: bannedBy
|
||||||
|
});
|
||||||
|
let update = tx.raw(createMySQLDuplicateKeyUpdate(
|
||||||
|
['external_reason', 'internal_reason']
|
||||||
|
));
|
||||||
|
|
||||||
|
return tx.raw(insert.toString() + update.toString());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
30
src/main.js
30
src/main.js
|
@ -2,6 +2,7 @@ import Config from './config';
|
||||||
import * as Switches from './switches';
|
import * as Switches from './switches';
|
||||||
import { eventlog } from './logger';
|
import { eventlog } from './logger';
|
||||||
require('source-map-support').install();
|
require('source-map-support').install();
|
||||||
|
import * as bannedChannels from './cli/banned-channels';
|
||||||
|
|
||||||
const LOGGER = require('@calzoneman/jsli')('main');
|
const LOGGER = require('@calzoneman/jsli')('main');
|
||||||
|
|
||||||
|
@ -28,10 +29,33 @@ if (!Config.get('debug')) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleCliCmd(cmd) {
|
||||||
|
try {
|
||||||
|
switch (cmd.command) {
|
||||||
|
case 'ban-channel':
|
||||||
|
return await bannedChannels.handleBanChannel(cmd);
|
||||||
|
default:
|
||||||
|
throw new Error(`Unrecognized command "${cmd.command}"`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return { status: 'error', error: String(error) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: this can probably just be part of servsock.js
|
// TODO: this can probably just be part of servsock.js
|
||||||
// servsock should also be refactored to send replies instead of
|
// servsock should also be refactored to send replies instead of
|
||||||
// relying solely on tailing logs
|
// relying solely on tailing logs
|
||||||
function handleLine(line) {
|
function handleLine(line, client) {
|
||||||
|
try {
|
||||||
|
let cmd = JSON.parse(line);
|
||||||
|
handleCliCmd(cmd).then(res => {
|
||||||
|
client.write(JSON.stringify(res) + '\n');
|
||||||
|
}).catch(error => {
|
||||||
|
LOGGER.error(`Unexpected error in handleCliCmd: ${error.stack}`);
|
||||||
|
});
|
||||||
|
} catch (_error) {
|
||||||
|
}
|
||||||
|
|
||||||
if (line === '/reload') {
|
if (line === '/reload') {
|
||||||
LOGGER.info('Reloading config');
|
LOGGER.info('Reloading config');
|
||||||
try {
|
try {
|
||||||
|
@ -81,9 +105,9 @@ if (Config.get('service-socket.enabled')) {
|
||||||
const ServiceSocket = require('./servsock');
|
const ServiceSocket = require('./servsock');
|
||||||
const sock = new ServiceSocket();
|
const sock = new ServiceSocket();
|
||||||
sock.init(
|
sock.init(
|
||||||
line => {
|
(line, client) => {
|
||||||
try {
|
try {
|
||||||
handleLine(line);
|
handleLine(line, client);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
LOGGER.error(
|
LOGGER.error(
|
||||||
'Error in UNIX socket command handler: %s',
|
'Error in UNIX socket command handler: %s',
|
||||||
|
|
|
@ -48,6 +48,7 @@ import { PartitionModule } from './partition/partitionmodule';
|
||||||
import { Gauge } from 'prom-client';
|
import { Gauge } from 'prom-client';
|
||||||
import { EmailController } from './controller/email';
|
import { EmailController } from './controller/email';
|
||||||
import { CaptchaController } from './controller/captcha';
|
import { CaptchaController } from './controller/captcha';
|
||||||
|
import { BannedChannelsController } from './controller/banned-channels';
|
||||||
|
|
||||||
var Server = function () {
|
var Server = function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -109,6 +110,11 @@ var Server = function () {
|
||||||
Config.getCaptchaConfig()
|
Config.getCaptchaConfig()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
self.bannedChannelsController = new BannedChannelsController(
|
||||||
|
self.db.channels,
|
||||||
|
globalMessageBus
|
||||||
|
);
|
||||||
|
|
||||||
// webserver init -----------------------------------------------------
|
// webserver init -----------------------------------------------------
|
||||||
const ioConfig = IOConfiguration.fromOldConfig(Config);
|
const ioConfig = IOConfiguration.fromOldConfig(Config);
|
||||||
const webConfig = WebConfiguration.fromOldConfig(Config);
|
const webConfig = WebConfiguration.fromOldConfig(Config);
|
||||||
|
|
|
@ -34,7 +34,7 @@ export default class ServiceSocket {
|
||||||
delete this.connections[id];
|
delete this.connections[id];
|
||||||
});
|
});
|
||||||
stream.on('data', (msg) => {
|
stream.on('data', (msg) => {
|
||||||
this.handler(msg.toString());
|
this.handler(msg.toString(), stream);
|
||||||
});
|
});
|
||||||
}).listen(this.socket);
|
}).listen(this.socket);
|
||||||
process.on('exit', this.closeServiceSocket.bind(this));
|
process.on('exit', this.closeServiceSocket.bind(this));
|
||||||
|
|
Loading…
Reference in a new issue