create Moderator role
This commit is contained in:
parent
a9922e3041
commit
9a7ca41eb9
|
@ -4,7 +4,7 @@
|
|||
* @flow
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import type { State } from '../reducers';
|
||||
|
@ -96,10 +96,60 @@ async function submitIPAction(
|
|||
callback(await resp.text());
|
||||
}
|
||||
|
||||
async function getModList(
|
||||
callback,
|
||||
) {
|
||||
const data = new FormData();
|
||||
data.append('modlist', true);
|
||||
const resp = await fetch('./admintools', {
|
||||
credentials: 'include',
|
||||
method: 'POST',
|
||||
body: data,
|
||||
});
|
||||
if (resp.ok) {
|
||||
callback(await resp.json());
|
||||
} else {
|
||||
callback([]);
|
||||
}
|
||||
}
|
||||
|
||||
async function submitRemMod(
|
||||
userId,
|
||||
callback,
|
||||
) {
|
||||
const data = new FormData();
|
||||
data.append('remmod', userId);
|
||||
const resp = await fetch('./admintools', {
|
||||
credentials: 'include',
|
||||
method: 'POST',
|
||||
body: data,
|
||||
});
|
||||
callback(resp.ok, await resp.text());
|
||||
}
|
||||
|
||||
async function submitMakeMod(
|
||||
userName,
|
||||
callback,
|
||||
) {
|
||||
const data = new FormData();
|
||||
data.append('makemod', userName);
|
||||
const resp = await fetch('./admintools', {
|
||||
credentials: 'include',
|
||||
method: 'POST',
|
||||
body: data,
|
||||
});
|
||||
if (resp.ok) {
|
||||
callback(await resp.json());
|
||||
} else {
|
||||
callback(await resp.text());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function Admintools({
|
||||
canvasId,
|
||||
canvases,
|
||||
userlvl,
|
||||
}) {
|
||||
const curDate = new Date();
|
||||
let day = curDate.getDate();
|
||||
|
@ -118,7 +168,9 @@ function Admintools({
|
|||
const [brcoords, selectBRCoords] = useState(keptState.brcoords);
|
||||
const [tlrcoords, selectTLRCoords] = useState(keptState.tlrcoords);
|
||||
const [brrcoords, selectBRRCoords] = useState(keptState.brrcoords);
|
||||
const [modName, selectModName] = useState(null);
|
||||
const [resp, setResp] = useState(null);
|
||||
const [modlist, setModList] = useState([]);
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
|
||||
let descAction;
|
||||
|
@ -136,6 +188,12 @@ function Admintools({
|
|||
// nothing
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (userlvl) {
|
||||
getModList((mods) => setModList(mods));
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<p style={{ textAlign: 'center', paddingLeft: '5%', paddingRight: '5%' }}>
|
||||
{resp && (
|
||||
|
@ -403,6 +461,8 @@ function Admintools({
|
|||
{(submitting) ? '...' : 'Submit'}
|
||||
</button>
|
||||
|
||||
{(userlvl === 1) && (
|
||||
<div>
|
||||
<br />
|
||||
<div className="modaldivider" />
|
||||
<h3 className="modaltitle">IP Actions</h3>
|
||||
|
@ -442,13 +502,103 @@ function Admintools({
|
|||
>
|
||||
{(submitting) ? '...' : 'Submit'}
|
||||
</button>
|
||||
<br />
|
||||
|
||||
<div className="modaldivider" />
|
||||
<h3 className="modaltitle">Manage Moderators</h3>
|
||||
<p className="modalcotext">
|
||||
Remove Moderator
|
||||
</p>
|
||||
{(modlist.length) ? (
|
||||
<span
|
||||
className="unblocklist"
|
||||
>
|
||||
{modlist.map((mod) => (
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => {
|
||||
if (submitting) {
|
||||
return;
|
||||
}
|
||||
setSubmitting(true);
|
||||
submitRemMod(mod[0], (success, ret) => {
|
||||
if (success) {
|
||||
setModList(
|
||||
modlist.filter((modl) => (modl[0] !== mod[0])),
|
||||
);
|
||||
}
|
||||
setSubmitting(false);
|
||||
setResp(ret);
|
||||
});
|
||||
}}
|
||||
>
|
||||
{`⦸ ${mod[0]} ${mod[1]}`}
|
||||
</div>
|
||||
))}
|
||||
</span>
|
||||
)
|
||||
: (
|
||||
<p className="modaltext">There are no mods</p>
|
||||
)}
|
||||
<br />
|
||||
|
||||
<p className="modalcotext">
|
||||
Assign new Mod
|
||||
</p>
|
||||
<p className="modalcotext">
|
||||
Enter UserName of new Mod:
|
||||
<input
|
||||
value={modName}
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
width: '100%',
|
||||
maxWidth: '20em',
|
||||
}}
|
||||
type="text"
|
||||
placeholder="User Name"
|
||||
onChange={(evt) => {
|
||||
const co = evt.target.value.trim();
|
||||
selectModName(co);
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (submitting) {
|
||||
return;
|
||||
}
|
||||
setSubmitting(true);
|
||||
submitMakeMod(
|
||||
modName,
|
||||
(ret) => {
|
||||
if (typeof ret === 'string') {
|
||||
setResp(ret);
|
||||
} else {
|
||||
setResp(`Made ${ret[1]} mod successfully.`);
|
||||
setModList([...modlist, ret]);
|
||||
}
|
||||
setSubmitting(false);
|
||||
},
|
||||
);
|
||||
}}
|
||||
>
|
||||
{(submitting) ? '...' : 'Submit'}
|
||||
</button>
|
||||
<br />
|
||||
<div className="modaldivider" />
|
||||
<br />
|
||||
</div>
|
||||
)}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
function mapStateToProps(state: State) {
|
||||
const { canvasId, canvases } = state.canvas;
|
||||
return { canvasId, canvases };
|
||||
const { userlvl } = state.user;
|
||||
return { canvasId, canvases, userlvl };
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(Admintools);
|
||||
|
|
|
@ -116,7 +116,7 @@ const UserAreaModal = ({
|
|||
</Suspense>
|
||||
</div>
|
||||
{userlvl && (
|
||||
<div label="Admintools">
|
||||
<div label={(userlvl === 1) ? 'Admintools' : 'Modtools'}>
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<Admintools />
|
||||
</Suspense>
|
||||
|
|
|
@ -223,7 +223,7 @@ export class ChatProvider {
|
|||
const { id } = user;
|
||||
const name = user.getName();
|
||||
|
||||
if (!user.isAdmin() && await cheapDetector(user.ip)) {
|
||||
if (!user.userlvl && await cheapDetector(user.ip)) {
|
||||
logger.info(
|
||||
`${name} / ${user.ip} tried to send chat message with proxy`,
|
||||
);
|
||||
|
@ -235,7 +235,7 @@ export class ChatProvider {
|
|||
return 'Couldn\'t send your message, pls log out and back in again.';
|
||||
}
|
||||
|
||||
if (user.isAdmin() && message.charAt(0) === '/') {
|
||||
if (message.charAt(0) === '/' && user.userlvl) {
|
||||
return this.adminCommands(message, channelId);
|
||||
}
|
||||
|
||||
|
|
406
src/core/adminfunctions.js
Normal file
406
src/core/adminfunctions.js
Normal file
|
@ -0,0 +1,406 @@
|
|||
/*
|
||||
* functions for admintools
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
/* eslint-disable no-await-in-loop */
|
||||
|
||||
import sharp from 'sharp';
|
||||
import Sequelize from 'sequelize';
|
||||
import redis from '../data/redis';
|
||||
|
||||
import { admintoolsLogger } from './logger';
|
||||
import { getIPv6Subnet } from '../utils/ip';
|
||||
import { Blacklist, Whitelist, RegUser } from '../data/models';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import canvases from './canvases.json';
|
||||
import {
|
||||
imageABGR2Canvas,
|
||||
protectCanvasArea,
|
||||
} from './Image';
|
||||
import rollbackCanvasArea from './rollback';
|
||||
|
||||
/*
|
||||
* Execute IP based actions (banning, whitelist, etc.)
|
||||
* @param action what to do with the ip
|
||||
* @param ip already sanizized ip
|
||||
* @return true if successful
|
||||
*/
|
||||
export async function executeIPAction(action: string, ips: string): string {
|
||||
const ipArray = ips.split('\n');
|
||||
let out = '';
|
||||
const splitRegExp = /\s+/;
|
||||
for (let i = 0; i < ipArray.length; i += 1) {
|
||||
let ip = ipArray[i].trim();
|
||||
const ipLine = ip.split(splitRegExp);
|
||||
if (ipLine.length === 7) {
|
||||
// logger output
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
ip = ipLine[2];
|
||||
}
|
||||
if (!ip || ip.length < 8 || ip.indexOf(' ') !== -1) {
|
||||
out += `Couln't parse ${action} ${ip}\n`;
|
||||
continue;
|
||||
}
|
||||
const ipKey = getIPv6Subnet(ip);
|
||||
const key = `isprox:${ipKey}`;
|
||||
|
||||
admintoolsLogger.info(`ADMINTOOLS: ${action} ${ip}`);
|
||||
switch (action) {
|
||||
case 'ban':
|
||||
await Blacklist.findOrCreate({
|
||||
where: { ip: ipKey },
|
||||
});
|
||||
await redis.setAsync(key, 'y', 'EX', 24 * 3600);
|
||||
break;
|
||||
case 'unban':
|
||||
await Blacklist.destroy({
|
||||
where: { ip: ipKey },
|
||||
});
|
||||
await redis.del(key);
|
||||
break;
|
||||
case 'whitelist':
|
||||
await Whitelist.findOrCreate({
|
||||
where: { ip: ipKey },
|
||||
});
|
||||
await redis.setAsync(key, 'n', 'EX', 24 * 3600);
|
||||
break;
|
||||
case 'unwhitelist':
|
||||
await Whitelist.destroy({
|
||||
where: { ip: ipKey },
|
||||
});
|
||||
await redis.del(key);
|
||||
break;
|
||||
default:
|
||||
out += `Failed to ${action} ${ip}\n`;
|
||||
}
|
||||
out += `Succseefully did ${action} ${ip}\n`;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute Image based actions (upload, protect, etc.)
|
||||
* @param action what to do with the image
|
||||
* @param file imagefile
|
||||
* @param coords coord sin X_Y format
|
||||
* @param canvasid numerical canvas id as string
|
||||
* @return [ret, msg] http status code and message
|
||||
*/
|
||||
export async function executeImageAction(
|
||||
action: string,
|
||||
file: Object,
|
||||
coords: string,
|
||||
canvasid: string,
|
||||
) {
|
||||
if (!coords) {
|
||||
return [403, 'Coordinates not defined'];
|
||||
}
|
||||
if (!canvasid) {
|
||||
return [403, 'canvasid not defined'];
|
||||
}
|
||||
|
||||
const splitCoords = coords.trim().split('_');
|
||||
if (splitCoords.length !== 2) {
|
||||
return [403, 'Invalid Coordinate Format'];
|
||||
}
|
||||
const [x, y] = splitCoords.map((z) => Math.floor(Number(z)));
|
||||
|
||||
const canvas = canvases[canvasid];
|
||||
|
||||
let error = null;
|
||||
if (Number.isNaN(x)) {
|
||||
error = 'x is not a valid number';
|
||||
} else if (Number.isNaN(y)) {
|
||||
error = 'y is not a valid number';
|
||||
} else if (!action) {
|
||||
error = 'No imageaction given';
|
||||
} else if (!canvas) {
|
||||
error = 'Invalid canvas selected';
|
||||
} else if (canvas.v) {
|
||||
error = 'Can not upload Image to 3D canvas';
|
||||
}
|
||||
if (error !== null) {
|
||||
return [403, error];
|
||||
}
|
||||
|
||||
const canvasMaxXY = canvas.size / 2;
|
||||
const canvasMinXY = -canvasMaxXY;
|
||||
if (x < canvasMinXY || y < canvasMinXY
|
||||
|| x >= canvasMaxXY || y >= canvasMaxXY) {
|
||||
return [403, 'Coordinates are outside of canvas'];
|
||||
}
|
||||
|
||||
const protect = (action === 'protect');
|
||||
const wipe = (action === 'wipe');
|
||||
|
||||
try {
|
||||
const { data, info } = await sharp(file.buffer)
|
||||
.ensureAlpha()
|
||||
.raw()
|
||||
.toBuffer({ resolveWithObject: true });
|
||||
|
||||
const pxlCount = await imageABGR2Canvas(
|
||||
canvasid,
|
||||
x, y,
|
||||
data,
|
||||
info.width, info.height,
|
||||
wipe, protect,
|
||||
);
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
admintoolsLogger.info(`ADMINTOOLS: Loaded image wth ${pxlCount} pixels to ${x}/${y}`);
|
||||
return [
|
||||
200,
|
||||
`Successfully loaded image wth ${pxlCount} pixels to ${x}/${y}`,
|
||||
];
|
||||
} catch {
|
||||
return [400, 'Can not read image file'];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute actions for protecting areas
|
||||
* @param action what to do
|
||||
* @param ulcoor coords of upper-left corner in X_Y format
|
||||
* @param brcoor coords of bottom-right corner in X_Y format
|
||||
* @param canvasid numerical canvas id as string
|
||||
* @return [ret, msg] http status code and message
|
||||
*/
|
||||
export async function executeProtAction(
|
||||
action: string,
|
||||
ulcoor: string,
|
||||
brcoor: string,
|
||||
canvasid: number,
|
||||
) {
|
||||
if (!ulcoor || !brcoor) {
|
||||
return [403, 'Not all coordinates defined'];
|
||||
}
|
||||
if (!canvasid) {
|
||||
return [403, 'canvasid not defined'];
|
||||
}
|
||||
|
||||
let splitCoords = ulcoor.trim().split('_');
|
||||
if (splitCoords.length !== 2) {
|
||||
return [403, 'Invalid Coordinate Format for top-left corner'];
|
||||
}
|
||||
const [x, y] = splitCoords.map((z) => Math.floor(Number(z)));
|
||||
splitCoords = brcoor.trim().split('_');
|
||||
if (splitCoords.length !== 2) {
|
||||
return [403, 'Invalid Coordinate Format for bottom-right corner'];
|
||||
}
|
||||
const [u, v] = splitCoords.map((z) => Math.floor(Number(z)));
|
||||
|
||||
const canvas = canvases[canvasid];
|
||||
|
||||
let error = null;
|
||||
if (Number.isNaN(x)) {
|
||||
error = 'x of top-left corner is not a valid number';
|
||||
} else if (Number.isNaN(y)) {
|
||||
error = 'y of top-left corner is not a valid number';
|
||||
} else if (Number.isNaN(u)) {
|
||||
error = 'x of bottom-right corner is not a valid number';
|
||||
} else if (Number.isNaN(v)) {
|
||||
error = 'y of bottom-right corner is not a valid number';
|
||||
} else if (u < x || v < y) {
|
||||
error = 'Corner coordinates are alligned wrong';
|
||||
} else if (!action) {
|
||||
error = 'No imageaction given';
|
||||
} else if (!canvas) {
|
||||
error = 'Invalid canvas selected';
|
||||
} else if (action !== 'protect' && action !== 'unprotect') {
|
||||
error = 'Invalid action (must be protect or unprotect)';
|
||||
}
|
||||
if (error !== null) {
|
||||
return [403, error];
|
||||
}
|
||||
|
||||
const canvasMaxXY = canvas.size / 2;
|
||||
const canvasMinXY = -canvasMaxXY;
|
||||
if (x < canvasMinXY || y < canvasMinXY
|
||||
|| x >= canvasMaxXY || y >= canvasMaxXY) {
|
||||
return [403, 'Coordinates of top-left corner are outside of canvas'];
|
||||
}
|
||||
if (u < canvasMinXY || v < canvasMinXY
|
||||
|| u >= canvasMaxXY || v >= canvasMaxXY) {
|
||||
return [403, 'Coordinates of bottom-right corner are outside of canvas'];
|
||||
}
|
||||
|
||||
const width = u - x + 1;
|
||||
const height = v - y + 1;
|
||||
const protect = action === 'protect';
|
||||
const pxlCount = await protectCanvasArea(
|
||||
canvasid,
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
protect,
|
||||
);
|
||||
admintoolsLogger.info(
|
||||
// eslint-disable-next-line max-len
|
||||
`ADMINTOOLS: Set protect to ${protect} for ${pxlCount} pixels at ${x} / ${y} with dimension ${width}x${height}`,
|
||||
);
|
||||
return [
|
||||
200,
|
||||
(protect)
|
||||
// eslint-disable-next-line max-len
|
||||
? `Successfully protected ${pxlCount} pixels at ${x} / ${y} with dimension ${width}x${height}`
|
||||
// eslint-disable-next-line max-len
|
||||
: `Soccessfully unprotected ${pxlCount} pixels at ${x} / ${y} with dimension ${width}x${height}`,
|
||||
];
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute rollback
|
||||
* @param date in format YYYYMMdd
|
||||
* @param ulcoor coords of upper-left corner in X_Y format
|
||||
* @param brcoor coords of bottom-right corner in X_Y format
|
||||
* @param canvasid numerical canvas id as string
|
||||
* @return [ret, msg] http status code and message
|
||||
*/
|
||||
export async function executeRollback(
|
||||
date: string,
|
||||
ulcoor: string,
|
||||
brcoor: string,
|
||||
canvasid: number,
|
||||
) {
|
||||
if (!ulcoor || !brcoor) {
|
||||
return [403, 'Not all coordinates defined'];
|
||||
}
|
||||
if (!canvasid) {
|
||||
return [403, 'canvasid not defined'];
|
||||
}
|
||||
|
||||
let splitCoords = ulcoor.trim().split('_');
|
||||
if (splitCoords.length !== 2) {
|
||||
return [403, 'Invalid Coordinate Format for top-left corner'];
|
||||
}
|
||||
const [x, y] = splitCoords.map((z) => Math.floor(Number(z)));
|
||||
splitCoords = brcoor.trim().split('_');
|
||||
if (splitCoords.length !== 2) {
|
||||
return [403, 'Invalid Coordinate Format for bottom-right corner'];
|
||||
}
|
||||
const [u, v] = splitCoords.map((z) => Math.floor(Number(z)));
|
||||
|
||||
const canvas = canvases[canvasid];
|
||||
|
||||
let error = null;
|
||||
if (Number.isNaN(x)) {
|
||||
error = 'x of top-left corner is not a valid number';
|
||||
} else if (Number.isNaN(y)) {
|
||||
error = 'y of top-left corner is not a valid number';
|
||||
} else if (Number.isNaN(u)) {
|
||||
error = 'x of bottom-right corner is not a valid number';
|
||||
} else if (Number.isNaN(v)) {
|
||||
error = 'y of bottom-right corner is not a valid number';
|
||||
} else if (u < x || v < y) {
|
||||
error = 'Corner coordinates are alligned wrong';
|
||||
} else if (!date) {
|
||||
error = 'No date given';
|
||||
} else if (Number.isNaN(Number(date))) {
|
||||
error = 'Invalid date';
|
||||
} else if (!canvas) {
|
||||
error = 'Invalid canvas selected';
|
||||
}
|
||||
if (error !== null) {
|
||||
return [403, error];
|
||||
}
|
||||
|
||||
const canvasMaxXY = canvas.size / 2;
|
||||
const canvasMinXY = -canvasMaxXY;
|
||||
if (x < canvasMinXY || y < canvasMinXY
|
||||
|| x >= canvasMaxXY || y >= canvasMaxXY) {
|
||||
return [403, 'Coordinates of top-left corner are outside of canvas'];
|
||||
}
|
||||
if (u < canvasMinXY || v < canvasMinXY
|
||||
|| u >= canvasMaxXY || v >= canvasMaxXY) {
|
||||
return [403, 'Coordinates of bottom-right corner are outside of canvas'];
|
||||
}
|
||||
|
||||
const width = u - x + 1;
|
||||
const height = v - y + 1;
|
||||
if (width * height > 1000000) {
|
||||
return [403, 'Can not rollback more than 1m pixels at onec'];
|
||||
}
|
||||
|
||||
const pxlCount = await rollbackCanvasArea(
|
||||
canvasid,
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
date,
|
||||
);
|
||||
admintoolsLogger.info(
|
||||
// eslint-disable-next-line max-len
|
||||
`ADMINTOOLS: Rollback to ${date} for ${pxlCount} pixels at ${x} / ${y} with dimension ${width}x${height}`,
|
||||
);
|
||||
return [
|
||||
200,
|
||||
// eslint-disable-next-line max-len
|
||||
`Successfully rolled back ${pxlCount} pixels at ${x} / ${y} with dimension ${width}x${height}`,
|
||||
];
|
||||
}
|
||||
|
||||
/*
|
||||
* Get list of mods
|
||||
* @return [[id1, name2], [id2, name2], ...] list
|
||||
*/
|
||||
export async function getModList() {
|
||||
const mods = await RegUser.findAll({
|
||||
where: Sequelize.where(Sequelize.literal('roles & 1'), '!=', 0),
|
||||
attributes: ['id', 'name'],
|
||||
raw: true,
|
||||
});
|
||||
return mods.map((mod) => [mod.id, mod.name]);
|
||||
}
|
||||
|
||||
export async function removeMod(userId) {
|
||||
if (Number.isNaN(userId)) {
|
||||
throw new Error('Invalid userId');
|
||||
}
|
||||
let user = null;
|
||||
try {
|
||||
user = await RegUser.findByPk(userId);
|
||||
} catch {
|
||||
throw new Error('Database error on remove mod');
|
||||
}
|
||||
if (!user) {
|
||||
throw new Error('User not found');
|
||||
}
|
||||
try {
|
||||
await user.update({
|
||||
isMod: false,
|
||||
});
|
||||
return `Moderation rights removed from user ${userId}`;
|
||||
} catch {
|
||||
throw new Error('Couldn\'t remove Mod from user');
|
||||
}
|
||||
}
|
||||
|
||||
export async function makeMod(name) {
|
||||
let user = null;
|
||||
try {
|
||||
user = await RegUser.findOne({
|
||||
where: {
|
||||
name,
|
||||
},
|
||||
});
|
||||
} catch {
|
||||
throw new Error(`Invalid user ${name}`);
|
||||
}
|
||||
if (!user) {
|
||||
throw new Error(`User ${name} not found`);
|
||||
}
|
||||
try {
|
||||
await user.update({
|
||||
isMod: true,
|
||||
});
|
||||
return [user.id, user.name];
|
||||
} catch {
|
||||
throw new Error('Couldn\'t remove Mod from user');
|
||||
}
|
||||
}
|
||||
|
|
@ -88,20 +88,22 @@ export async function drawByOffset(
|
|||
}
|
||||
}
|
||||
|
||||
const isAdmin = (user.userlvl === 1);
|
||||
const setColor = await RedisCanvas.getPixelByOffset(canvasId, i, j, offset);
|
||||
|
||||
if (setColor & 0x80
|
||||
/* 3D Canvas Minecraft Avatars */
|
||||
// && x >= 96 && x <= 128 && z >= 35 && z <= 100
|
||||
// 96 - 128 on x
|
||||
// 32 - 128 on z
|
||||
|| (canvas.v && i === 19 && j >= 17 && j < 20 && !user.isAdmin())
|
||||
|| (canvas.v && i === 19 && j >= 17 && j < 20 && !isAdmin)
|
||||
) {
|
||||
// protected pixel
|
||||
throw new Error(8);
|
||||
}
|
||||
|
||||
coolDown = (setColor & 0x3F) < canvas.cli ? canvas.bcd : canvas.pcd;
|
||||
if (user.isAdmin()) {
|
||||
if (isAdmin) {
|
||||
coolDown = 0.0;
|
||||
} else if (rpgEvent.success) {
|
||||
if (rpgEvent.success === 1) {
|
||||
|
@ -262,10 +264,11 @@ export async function drawByCoords(
|
|||
}
|
||||
}
|
||||
|
||||
const isAdmin = (user.userlvl === 1);
|
||||
const setColor = await RedisCanvas.getPixel(canvasId, x, y, z);
|
||||
|
||||
let coolDown = (setColor & 0x3F) < canvas.cli ? canvas.bcd : canvas.pcd;
|
||||
if (user.isAdmin()) {
|
||||
if (isAdmin) {
|
||||
coolDown = 0.0;
|
||||
} else if (rpgEvent.success) {
|
||||
if (rpgEvent.success === 1) {
|
||||
|
@ -293,7 +296,7 @@ export async function drawByCoords(
|
|||
if (setColor & 0x80
|
||||
|| (canvas.v
|
||||
&& x >= 96 && x <= 128 && z >= 35 && z <= 100
|
||||
&& !user.isAdmin())
|
||||
&& !isAdmin)
|
||||
) {
|
||||
logger.info(`${user.ip} tried to set on protected pixel (${x}, ${y})`);
|
||||
return {
|
||||
|
@ -338,10 +341,6 @@ export function drawSafeByCoords(
|
|||
y: number,
|
||||
z: number = null,
|
||||
): Promise<Cell> {
|
||||
if (user.isAdmin()) {
|
||||
return drawByCoords(user, canvasId, color, x, y, z);
|
||||
}
|
||||
|
||||
// can just check for one unique occurence,
|
||||
// we use ip, because id for logged out users is
|
||||
// always null
|
||||
|
@ -379,10 +378,6 @@ export function drawSafeByOffset(
|
|||
j: number,
|
||||
offset: number,
|
||||
): Promise<Cell> {
|
||||
if (user.isAdmin()) {
|
||||
return drawByOffset(user, canvasId, color, i, j, offset);
|
||||
}
|
||||
|
||||
// can just check for one unique occurence,
|
||||
// we use ip, because id for logged out users is
|
||||
// always null
|
||||
|
|
|
@ -48,5 +48,19 @@ export const proxyLogger = createLogger({
|
|||
],
|
||||
});
|
||||
|
||||
export const admintoolsLogger = createLogger({
|
||||
format: format.printf(({ message }) => message),
|
||||
transports: [
|
||||
new DailyRotateFile({
|
||||
level: 'info',
|
||||
filename: './log/admintools-%DATE%.log',
|
||||
maxSize: '20m',
|
||||
maxFiles: '14d',
|
||||
colorize: false,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
|
||||
export default logger;
|
||||
|
|
|
@ -29,6 +29,13 @@ const RegUser = Model.define('User', {
|
|||
allowNull: false,
|
||||
},
|
||||
|
||||
// currently just moderator
|
||||
roles: {
|
||||
type: DataType.TINYINT,
|
||||
allowNull: false,
|
||||
defaultValue: 0,
|
||||
},
|
||||
|
||||
// null if external oauth authentification
|
||||
password: {
|
||||
type: DataType.CHAR(60),
|
||||
|
@ -125,6 +132,10 @@ const RegUser = Model.define('User', {
|
|||
blockDm(): boolean {
|
||||
return this.blocks & 0x01;
|
||||
},
|
||||
|
||||
isMod(): boolean {
|
||||
return this.roles & 0x01;
|
||||
},
|
||||
},
|
||||
|
||||
setterMethods: {
|
||||
|
@ -143,6 +154,11 @@ const RegUser = Model.define('User', {
|
|||
this.setDataValue('blocks', val);
|
||||
},
|
||||
|
||||
isMod(num: boolean) {
|
||||
const val = (num) ? (this.roles | 0x01) : (this.roles & ~0x01);
|
||||
this.setDataValue('roles', val);
|
||||
},
|
||||
|
||||
password(value: string) {
|
||||
if (value) this.setDataValue('password', generateHash(value));
|
||||
},
|
||||
|
|
|
@ -22,7 +22,14 @@ class User {
|
|||
ip: string;
|
||||
wait: ?number;
|
||||
regUser: Object;
|
||||
channels: Array;
|
||||
channels: Object;
|
||||
blocked: Array;
|
||||
/*
|
||||
* 0: nothing
|
||||
* 1: Admin
|
||||
* 2: Mod
|
||||
*/
|
||||
userlvl: number;
|
||||
|
||||
constructor(id: string = null, ip: string = '127.0.0.1') {
|
||||
// id should stay null if unregistered
|
||||
|
@ -30,6 +37,7 @@ class User {
|
|||
this.ip = ip;
|
||||
this.channels = {};
|
||||
this.blocked = [];
|
||||
this.userlvl = 0;
|
||||
this.ipSub = getIPv6Subnet(ip);
|
||||
this.wait = null;
|
||||
// following gets populated by passport
|
||||
|
@ -54,6 +62,14 @@ class User {
|
|||
setRegUser(reguser) {
|
||||
this.regUser = reguser;
|
||||
this.id = reguser.id;
|
||||
|
||||
if (this.regUser.isMod) {
|
||||
this.userlvl = 2;
|
||||
}
|
||||
if (ADMIN_IDS.includes(this.id)) {
|
||||
this.userlvl = 1;
|
||||
}
|
||||
|
||||
if (reguser.channel) {
|
||||
for (let i = 0; i < reguser.channel.length; i += 1) {
|
||||
const {
|
||||
|
@ -139,7 +155,7 @@ class User {
|
|||
async incrementPixelcount(): Promise<boolean> {
|
||||
const { id } = this;
|
||||
if (!id) return false;
|
||||
if (this.isAdmin()) return false;
|
||||
if (this.userlvl === 1) return false;
|
||||
try {
|
||||
await RegUser.update({
|
||||
totalPixels: Sequelize.literal('totalPixels + 1'),
|
||||
|
@ -156,7 +172,7 @@ class User {
|
|||
async getTotalPixels(): Promise<number> {
|
||||
const { id } = this;
|
||||
if (!id) return 0;
|
||||
if (this.isAdmin()) return 100000;
|
||||
if (this.userlvl === 1) return 100000;
|
||||
if (this.regUser) {
|
||||
return this.regUser.totalPixels;
|
||||
}
|
||||
|
@ -196,10 +212,6 @@ class User {
|
|||
return true;
|
||||
}
|
||||
|
||||
isAdmin(): boolean {
|
||||
return ADMIN_IDS.includes(this.id);
|
||||
}
|
||||
|
||||
getUserData(): Object {
|
||||
if (this.regUser == null) {
|
||||
return {
|
||||
|
@ -218,7 +230,9 @@ class User {
|
|||
blocked: this.blocked,
|
||||
};
|
||||
}
|
||||
const { regUser } = this;
|
||||
const {
|
||||
regUser, userlvl, channels, blocked,
|
||||
} = this;
|
||||
return {
|
||||
name: regUser.name,
|
||||
mailVerified: regUser.mailVerified,
|
||||
|
@ -230,9 +244,9 @@ class User {
|
|||
ranking: regUser.ranking,
|
||||
dailyRanking: regUser.dailyRanking,
|
||||
mailreg: !!(regUser.password),
|
||||
userlvl: this.isAdmin() ? 1 : 0,
|
||||
channels: this.channels,
|
||||
blocked: this.blocked,
|
||||
userlvl,
|
||||
channels,
|
||||
blocked,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ export type UserState = {
|
|||
isOnMobile: boolean,
|
||||
// small notifications for received cooldown
|
||||
notification: string,
|
||||
// 1: Admin, 0: ordinary user
|
||||
// 1: Admin, 2: Mod, 0: ordinary user
|
||||
userlvl: number,
|
||||
// regExp for detecting ping
|
||||
nameRegExp: RegExp,
|
||||
|
|
|
@ -6,30 +6,27 @@
|
|||
*
|
||||
*/
|
||||
|
||||
/* eslint-disable no-await-in-loop */
|
||||
|
||||
import express from 'express';
|
||||
import expressLimiter from 'express-limiter';
|
||||
import type { Request, Response } from 'express';
|
||||
import bodyParser from 'body-parser';
|
||||
import sharp from 'sharp';
|
||||
import multer from 'multer';
|
||||
|
||||
import { getIPFromRequest, getIPv6Subnet } from '../utils/ip';
|
||||
import { getIPFromRequest } from '../utils/ip';
|
||||
import redis from '../data/redis';
|
||||
import session from '../core/session';
|
||||
import passport from '../core/passport';
|
||||
import logger from '../core/logger';
|
||||
import { Blacklist, Whitelist } from '../data/models';
|
||||
|
||||
import { admintoolsLogger } from '../core/logger';
|
||||
import { MINUTE } from '../core/constants';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import canvases from './canvases.json';
|
||||
import {
|
||||
imageABGR2Canvas,
|
||||
protectCanvasArea,
|
||||
} from '../core/Image';
|
||||
import rollbackCanvasArea from '../core/rollback';
|
||||
executeIPAction,
|
||||
executeImageAction,
|
||||
executeProtAction,
|
||||
executeRollback,
|
||||
getModList,
|
||||
removeMod,
|
||||
makeMod,
|
||||
} from '../core/adminfunctions';
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
|
@ -50,6 +47,7 @@ const upload = multer({
|
|||
|
||||
/*
|
||||
* rate limiting to prevent bruteforce attacks
|
||||
* TODO: do that with nginx
|
||||
*/
|
||||
router.use('/',
|
||||
limiter({
|
||||
|
@ -61,7 +59,7 @@ router.use('/',
|
|||
|
||||
|
||||
/*
|
||||
* make sure User is logged in and admin
|
||||
* make sure User is logged in and mod or admin
|
||||
*/
|
||||
router.use(session);
|
||||
router.use(passport.initialize());
|
||||
|
@ -69,350 +67,31 @@ router.use(passport.session());
|
|||
router.use(async (req, res, next) => {
|
||||
const ip = getIPFromRequest(req);
|
||||
if (!req.user) {
|
||||
logger.info(`ADMINTOOLS: ${ip} tried to access admintools without login`);
|
||||
admintoolsLogger.info(`ADMINTOOLS: ${ip} tried to access admintools without login`);
|
||||
res.status(403).send('You are not logged in');
|
||||
return;
|
||||
}
|
||||
if (!req.user.isAdmin()) {
|
||||
logger.info(
|
||||
/*
|
||||
* 1 = Admin
|
||||
* 2 = Mod
|
||||
*/
|
||||
if (!req.user.userlvl) {
|
||||
admintoolsLogger.info(
|
||||
`ADMINTOOLS: ${ip} / ${req.user.id} tried to access admintools`,
|
||||
);
|
||||
res.status(403).send('You are not allowed to access this page');
|
||||
return;
|
||||
}
|
||||
logger.info(
|
||||
admintoolsLogger.info(
|
||||
`ADMINTOOLS: ${req.user.id} / ${req.user.regUser.name} is using admintools`,
|
||||
);
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
* Execute IP based actions (banning, whitelist, etc.)
|
||||
* @param action what to do with the ip
|
||||
* @param ip already sanizized ip
|
||||
* @return true if successful
|
||||
*/
|
||||
async function executeIPAction(action: string, ips: string): boolean {
|
||||
const ipArray = ips.split('\n');
|
||||
let out = '';
|
||||
const splitRegExp = /\s+/;
|
||||
for (let i = 0; i < ipArray.length; i += 1) {
|
||||
let ip = ipArray[i].trim();
|
||||
const ipLine = ip.split(splitRegExp);
|
||||
if (ipLine.length === 7) {
|
||||
// logger output
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
ip = ipLine[2];
|
||||
}
|
||||
if (!ip || ip.length < 8 || ip.indexOf(' ') !== -1) {
|
||||
out += `Couln't parse ${action} ${ip}\n`;
|
||||
continue;
|
||||
}
|
||||
const ipKey = getIPv6Subnet(ip);
|
||||
const key = `isprox:${ipKey}`;
|
||||
|
||||
logger.info(`ADMINTOOLS: ${action} ${ip}`);
|
||||
switch (action) {
|
||||
case 'ban':
|
||||
await Blacklist.findOrCreate({
|
||||
where: { ip: ipKey },
|
||||
});
|
||||
await redis.setAsync(key, 'y', 'EX', 24 * 3600);
|
||||
break;
|
||||
case 'unban':
|
||||
await Blacklist.destroy({
|
||||
where: { ip: ipKey },
|
||||
});
|
||||
await redis.del(key);
|
||||
break;
|
||||
case 'whitelist':
|
||||
await Whitelist.findOrCreate({
|
||||
where: { ip: ipKey },
|
||||
});
|
||||
await redis.setAsync(key, 'n', 'EX', 24 * 3600);
|
||||
break;
|
||||
case 'unwhitelist':
|
||||
await Whitelist.destroy({
|
||||
where: { ip: ipKey },
|
||||
});
|
||||
await redis.del(key);
|
||||
break;
|
||||
default:
|
||||
out += `Failed to ${action} ${ip}\n`;
|
||||
}
|
||||
out += `Succseefully did ${action} ${ip}\n`;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute Image based actions (upload, protect, etc.)
|
||||
* @param action what to do with the image
|
||||
* @param file imagefile
|
||||
* @param coords coord sin X_Y format
|
||||
* @param canvasid numerical canvas id as string
|
||||
* @return [ret, msg] http status code and message
|
||||
*/
|
||||
async function executeImageAction(
|
||||
action: string,
|
||||
file: Object,
|
||||
coords: string,
|
||||
canvasid: string,
|
||||
) {
|
||||
if (!coords) {
|
||||
return [403, 'Coordinates not defined'];
|
||||
}
|
||||
if (!canvasid) {
|
||||
return [403, 'canvasid not defined'];
|
||||
}
|
||||
|
||||
const splitCoords = coords.trim().split('_');
|
||||
if (splitCoords.length !== 2) {
|
||||
return [403, 'Invalid Coordinate Format'];
|
||||
}
|
||||
const [x, y] = splitCoords.map((z) => Math.floor(Number(z)));
|
||||
|
||||
const canvas = canvases[canvasid];
|
||||
|
||||
let error = null;
|
||||
if (Number.isNaN(x)) {
|
||||
error = 'x is not a valid number';
|
||||
} else if (Number.isNaN(y)) {
|
||||
error = 'y is not a valid number';
|
||||
} else if (!action) {
|
||||
error = 'No imageaction given';
|
||||
} else if (!canvas) {
|
||||
error = 'Invalid canvas selected';
|
||||
} else if (canvas.v) {
|
||||
error = 'Can not upload Image to 3D canvas';
|
||||
}
|
||||
if (error !== null) {
|
||||
return [403, error];
|
||||
}
|
||||
|
||||
const canvasMaxXY = canvas.size / 2;
|
||||
const canvasMinXY = -canvasMaxXY;
|
||||
if (x < canvasMinXY || y < canvasMinXY
|
||||
|| x >= canvasMaxXY || y >= canvasMaxXY) {
|
||||
return [403, 'Coordinates are outside of canvas'];
|
||||
}
|
||||
|
||||
const protect = (action === 'protect');
|
||||
const wipe = (action === 'wipe');
|
||||
|
||||
try {
|
||||
const { data, info } = await sharp(file.buffer)
|
||||
.ensureAlpha()
|
||||
.raw()
|
||||
.toBuffer({ resolveWithObject: true });
|
||||
|
||||
const pxlCount = await imageABGR2Canvas(
|
||||
canvasid,
|
||||
x, y,
|
||||
data,
|
||||
info.width, info.height,
|
||||
wipe, protect,
|
||||
);
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
logger.info(`ADMINTOOLS: Loaded image wth ${pxlCount} pixels to ${x}/${y}`);
|
||||
return [
|
||||
200,
|
||||
`Successfully loaded image wth ${pxlCount} pixels to ${x}/${y}`,
|
||||
];
|
||||
} catch {
|
||||
return [400, 'Can not read image file'];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute actions for protecting areas
|
||||
* @param action what to do
|
||||
* @param ulcoor coords of upper-left corner in X_Y format
|
||||
* @param brcoor coords of bottom-right corner in X_Y format
|
||||
* @param canvasid numerical canvas id as string
|
||||
* @return [ret, msg] http status code and message
|
||||
*/
|
||||
async function executeProtAction(
|
||||
action: string,
|
||||
ulcoor: string,
|
||||
brcoor: string,
|
||||
canvasid: number,
|
||||
) {
|
||||
if (!ulcoor || !brcoor) {
|
||||
return [403, 'Not all coordinates defined'];
|
||||
}
|
||||
if (!canvasid) {
|
||||
return [403, 'canvasid not defined'];
|
||||
}
|
||||
|
||||
let splitCoords = ulcoor.trim().split('_');
|
||||
if (splitCoords.length !== 2) {
|
||||
return [403, 'Invalid Coordinate Format for top-left corner'];
|
||||
}
|
||||
const [x, y] = splitCoords.map((z) => Math.floor(Number(z)));
|
||||
splitCoords = brcoor.trim().split('_');
|
||||
if (splitCoords.length !== 2) {
|
||||
return [403, 'Invalid Coordinate Format for bottom-right corner'];
|
||||
}
|
||||
const [u, v] = splitCoords.map((z) => Math.floor(Number(z)));
|
||||
|
||||
const canvas = canvases[canvasid];
|
||||
|
||||
let error = null;
|
||||
if (Number.isNaN(x)) {
|
||||
error = 'x of top-left corner is not a valid number';
|
||||
} else if (Number.isNaN(y)) {
|
||||
error = 'y of top-left corner is not a valid number';
|
||||
} else if (Number.isNaN(u)) {
|
||||
error = 'x of bottom-right corner is not a valid number';
|
||||
} else if (Number.isNaN(v)) {
|
||||
error = 'y of bottom-right corner is not a valid number';
|
||||
} else if (u < x || v < y) {
|
||||
error = 'Corner coordinates are alligned wrong';
|
||||
} else if (!action) {
|
||||
error = 'No imageaction given';
|
||||
} else if (!canvas) {
|
||||
error = 'Invalid canvas selected';
|
||||
} else if (action !== 'protect' && action !== 'unprotect') {
|
||||
error = 'Invalid action (must be protect or unprotect)';
|
||||
}
|
||||
if (error !== null) {
|
||||
return [403, error];
|
||||
}
|
||||
|
||||
const canvasMaxXY = canvas.size / 2;
|
||||
const canvasMinXY = -canvasMaxXY;
|
||||
if (x < canvasMinXY || y < canvasMinXY
|
||||
|| x >= canvasMaxXY || y >= canvasMaxXY) {
|
||||
return [403, 'Coordinates of top-left corner are outside of canvas'];
|
||||
}
|
||||
if (u < canvasMinXY || v < canvasMinXY
|
||||
|| u >= canvasMaxXY || v >= canvasMaxXY) {
|
||||
return [403, 'Coordinates of bottom-right corner are outside of canvas'];
|
||||
}
|
||||
|
||||
const width = u - x + 1;
|
||||
const height = v - y + 1;
|
||||
const protect = action === 'protect';
|
||||
const pxlCount = await protectCanvasArea(
|
||||
canvasid,
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
protect,
|
||||
);
|
||||
logger.info(
|
||||
// eslint-disable-next-line max-len
|
||||
`ADMINTOOLS: Set protect to ${protect} for ${pxlCount} pixels at ${x} / ${y} with dimension ${width}x${height}`,
|
||||
);
|
||||
return [
|
||||
200,
|
||||
(protect)
|
||||
// eslint-disable-next-line max-len
|
||||
? `Successfully protected ${pxlCount} pixels at ${x} / ${y} with dimension ${width}x${height}`
|
||||
// eslint-disable-next-line max-len
|
||||
: `Soccessfully unprotected ${pxlCount} pixels at ${x} / ${y} with dimension ${width}x${height}`,
|
||||
];
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute rollback
|
||||
* @param date in format YYYYMMdd
|
||||
* @param ulcoor coords of upper-left corner in X_Y format
|
||||
* @param brcoor coords of bottom-right corner in X_Y format
|
||||
* @param canvasid numerical canvas id as string
|
||||
* @return [ret, msg] http status code and message
|
||||
*/
|
||||
async function executeRollback(
|
||||
date: string,
|
||||
ulcoor: string,
|
||||
brcoor: string,
|
||||
canvasid: number,
|
||||
) {
|
||||
if (!ulcoor || !brcoor) {
|
||||
return [403, 'Not all coordinates defined'];
|
||||
}
|
||||
if (!canvasid) {
|
||||
return [403, 'canvasid not defined'];
|
||||
}
|
||||
|
||||
let splitCoords = ulcoor.trim().split('_');
|
||||
if (splitCoords.length !== 2) {
|
||||
return [403, 'Invalid Coordinate Format for top-left corner'];
|
||||
}
|
||||
const [x, y] = splitCoords.map((z) => Math.floor(Number(z)));
|
||||
splitCoords = brcoor.trim().split('_');
|
||||
if (splitCoords.length !== 2) {
|
||||
return [403, 'Invalid Coordinate Format for bottom-right corner'];
|
||||
}
|
||||
const [u, v] = splitCoords.map((z) => Math.floor(Number(z)));
|
||||
|
||||
const canvas = canvases[canvasid];
|
||||
|
||||
let error = null;
|
||||
if (Number.isNaN(x)) {
|
||||
error = 'x of top-left corner is not a valid number';
|
||||
} else if (Number.isNaN(y)) {
|
||||
error = 'y of top-left corner is not a valid number';
|
||||
} else if (Number.isNaN(u)) {
|
||||
error = 'x of bottom-right corner is not a valid number';
|
||||
} else if (Number.isNaN(v)) {
|
||||
error = 'y of bottom-right corner is not a valid number';
|
||||
} else if (u < x || v < y) {
|
||||
error = 'Corner coordinates are alligned wrong';
|
||||
} else if (!date) {
|
||||
error = 'No date given';
|
||||
} else if (Number.isNaN(Number(date))) {
|
||||
error = 'Invalid date';
|
||||
} else if (!canvas) {
|
||||
error = 'Invalid canvas selected';
|
||||
}
|
||||
if (error !== null) {
|
||||
return [403, error];
|
||||
}
|
||||
|
||||
const canvasMaxXY = canvas.size / 2;
|
||||
const canvasMinXY = -canvasMaxXY;
|
||||
if (x < canvasMinXY || y < canvasMinXY
|
||||
|| x >= canvasMaxXY || y >= canvasMaxXY) {
|
||||
return [403, 'Coordinates of top-left corner are outside of canvas'];
|
||||
}
|
||||
if (u < canvasMinXY || v < canvasMinXY
|
||||
|| u >= canvasMaxXY || v >= canvasMaxXY) {
|
||||
return [403, 'Coordinates of bottom-right corner are outside of canvas'];
|
||||
}
|
||||
|
||||
const width = u - x + 1;
|
||||
const height = v - y + 1;
|
||||
if (width * height > 1000000) {
|
||||
return [403, 'Can not rollback more than 1m pixels at onec'];
|
||||
}
|
||||
|
||||
const pxlCount = await rollbackCanvasArea(
|
||||
canvasid,
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
date,
|
||||
);
|
||||
logger.info(
|
||||
// eslint-disable-next-line max-len
|
||||
`ADMINTOOLS: Rollback to ${date} for ${pxlCount} pixels at ${x} / ${y} with dimension ${width}x${height}`,
|
||||
);
|
||||
return [
|
||||
200,
|
||||
// eslint-disable-next-line max-len
|
||||
`Successfully rolled back ${pxlCount} pixels at ${x} / ${y} with dimension ${width}x${height}`,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Check for POST parameters,
|
||||
* Post for mod + admin
|
||||
*/
|
||||
router.post('/', upload.single('image'), async (req, res, next) => {
|
||||
try {
|
||||
|
@ -426,10 +105,6 @@ router.post('/', upload.single('image'), async (req, res, next) => {
|
|||
);
|
||||
res.status(ret).send(msg);
|
||||
return;
|
||||
} if (req.body.ipaction) {
|
||||
const ret = await executeIPAction(req.body.ipaction, req.body.ip);
|
||||
res.status(200).send(ret);
|
||||
return;
|
||||
} if (req.body.protaction) {
|
||||
const {
|
||||
protaction, ulcoor, brcoor, canvasid,
|
||||
|
@ -465,23 +140,52 @@ router.post('/', upload.single('image'), async (req, res, next) => {
|
|||
|
||||
|
||||
/*
|
||||
* Check GET parameters for action to execute
|
||||
* just admins past here, no Mods
|
||||
*/
|
||||
router.get('/', async (req: Request, res: Response, next) => {
|
||||
try {
|
||||
const { ip, ipaction } = req.query;
|
||||
if (!ipaction) {
|
||||
router.use(async (req, res, next) => {
|
||||
if (req.user.userlvl !== 1) {
|
||||
res.status(403).send('Just admins can do that');
|
||||
return;
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
/*
|
||||
* Post just for admin
|
||||
*/
|
||||
router.post('/', async (req, res, next) => {
|
||||
try {
|
||||
if (req.body.ipaction) {
|
||||
const ret = await executeIPAction(req.body.ipaction, req.body.ip);
|
||||
res.status(200).send(ret);
|
||||
return;
|
||||
}
|
||||
if (!ip) {
|
||||
res.status(400).json({ errors: 'invalid ip' });
|
||||
if (req.body.modlist) {
|
||||
const ret = await getModList();
|
||||
res.status(200);
|
||||
res.json(ret);
|
||||
return;
|
||||
}
|
||||
|
||||
const ret = await executeIPAction(ipaction, ip);
|
||||
|
||||
res.json({ ipaction: 'success', messages: ret.split('\n') });
|
||||
if (req.body.remmod) {
|
||||
try {
|
||||
const ret = await removeMod(req.body.remmod);
|
||||
res.status(200).send(ret);
|
||||
} catch (e) {
|
||||
res.status(400).send(e.message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (req.body.makemod) {
|
||||
try {
|
||||
const ret = await makeMod(req.body.makemod);
|
||||
res.status(200);
|
||||
res.json(ret);
|
||||
} catch (e) {
|
||||
res.status(400).send(e.message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
next();
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user