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 Flags = require("../flags");
|
||||
var util = require("../utilities");
|
||||
// TODO: I think newer knex has native support for this
|
||||
import { createMySQLDuplicateKeyUpdate } from '../util/on-duplicate-key-update';
|
||||
import Config from '../config';
|
||||
|
||||
|
@ -746,5 +747,26 @@ module.exports = {
|
|||
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 { eventlog } from './logger';
|
||||
require('source-map-support').install();
|
||||
import * as bannedChannels from './cli/banned-channels';
|
||||
|
||||
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
|
||||
// servsock should also be refactored to send replies instead of
|
||||
// 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') {
|
||||
LOGGER.info('Reloading config');
|
||||
try {
|
||||
|
@ -81,9 +105,9 @@ if (Config.get('service-socket.enabled')) {
|
|||
const ServiceSocket = require('./servsock');
|
||||
const sock = new ServiceSocket();
|
||||
sock.init(
|
||||
line => {
|
||||
(line, client) => {
|
||||
try {
|
||||
handleLine(line);
|
||||
handleLine(line, client);
|
||||
} catch (error) {
|
||||
LOGGER.error(
|
||||
'Error in UNIX socket command handler: %s',
|
||||
|
|
|
@ -48,6 +48,7 @@ import { PartitionModule } from './partition/partitionmodule';
|
|||
import { Gauge } from 'prom-client';
|
||||
import { EmailController } from './controller/email';
|
||||
import { CaptchaController } from './controller/captcha';
|
||||
import { BannedChannelsController } from './controller/banned-channels';
|
||||
|
||||
var Server = function () {
|
||||
var self = this;
|
||||
|
@ -109,6 +110,11 @@ var Server = function () {
|
|||
Config.getCaptchaConfig()
|
||||
);
|
||||
|
||||
self.bannedChannelsController = new BannedChannelsController(
|
||||
self.db.channels,
|
||||
globalMessageBus
|
||||
);
|
||||
|
||||
// webserver init -----------------------------------------------------
|
||||
const ioConfig = IOConfiguration.fromOldConfig(Config);
|
||||
const webConfig = WebConfiguration.fromOldConfig(Config);
|
||||
|
|
|
@ -34,7 +34,7 @@ export default class ServiceSocket {
|
|||
delete this.connections[id];
|
||||
});
|
||||
stream.on('data', (msg) => {
|
||||
this.handler(msg.toString());
|
||||
this.handler(msg.toString(), stream);
|
||||
});
|
||||
}).listen(this.socket);
|
||||
process.on('exit', this.closeServiceSocket.bind(this));
|
||||
|
|
Loading…
Reference in a new issue