add canvas cleaner
This commit is contained in:
parent
9122f3e0a0
commit
3a14699c6b
|
@ -7,14 +7,17 @@ import React, { useState, useEffect } from 'react';
|
|||
import { useSelector, shallowEqual } from 'react-redux';
|
||||
import { t } from 'ttag';
|
||||
|
||||
import useInterval from './hooks/useInterval';
|
||||
import { getToday, dateToString } from '../core/utils';
|
||||
|
||||
const keptState = {
|
||||
coords: null,
|
||||
tlcoords: null,
|
||||
brcoords: null,
|
||||
tlrcoords: null,
|
||||
brrcoords: null,
|
||||
coords: '',
|
||||
tlcoords: '',
|
||||
brcoords: '',
|
||||
tlrcoords: '',
|
||||
brrcoords: '',
|
||||
tlccoords: '',
|
||||
brccoords: '',
|
||||
};
|
||||
|
||||
async function submitImageAction(
|
||||
|
@ -80,6 +83,26 @@ async function submitRollback(
|
|||
callback(await resp.text());
|
||||
}
|
||||
|
||||
async function submitCanvasCleaner(
|
||||
action,
|
||||
canvas,
|
||||
tlcoords,
|
||||
brcoords,
|
||||
callback,
|
||||
) {
|
||||
const data = new FormData();
|
||||
data.append('cleaneraction', action);
|
||||
data.append('canvasid', canvas);
|
||||
data.append('ulcoor', tlcoords);
|
||||
data.append('brcoor', brcoords);
|
||||
const resp = await fetch('./api/modtools', {
|
||||
credentials: 'include',
|
||||
method: 'POST',
|
||||
body: data,
|
||||
});
|
||||
callback(await resp.text());
|
||||
}
|
||||
|
||||
async function submitIPAction(
|
||||
action,
|
||||
callback,
|
||||
|
@ -113,6 +136,41 @@ async function getModList(
|
|||
}
|
||||
}
|
||||
|
||||
async function getCleanerStats(
|
||||
callback,
|
||||
) {
|
||||
const data = new FormData();
|
||||
data.append('cleanerstat', true);
|
||||
const resp = await fetch('./api/modtools', {
|
||||
credentials: 'include',
|
||||
method: 'POST',
|
||||
body: data,
|
||||
});
|
||||
if (resp.ok) {
|
||||
callback(await resp.json());
|
||||
} else {
|
||||
callback({
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function getCleanerCancel(
|
||||
callback,
|
||||
) {
|
||||
const data = new FormData();
|
||||
data.append('cleanercancel', true);
|
||||
const resp = await fetch('./api/modtools', {
|
||||
credentials: 'include',
|
||||
method: 'POST',
|
||||
body: data,
|
||||
});
|
||||
if (resp.ok) {
|
||||
callback(await resp.text());
|
||||
} else {
|
||||
callback('');
|
||||
}
|
||||
}
|
||||
|
||||
async function submitRemMod(
|
||||
userId,
|
||||
callback,
|
||||
|
@ -151,6 +209,7 @@ function Modtools() {
|
|||
|
||||
const [selectedCanvas, selectCanvas] = useState(0);
|
||||
const [imageAction, selectImageAction] = useState('build');
|
||||
const [cleanAction, selectCleanAction] = useState('spare');
|
||||
const [iPAction, selectIPAction] = useState('ban');
|
||||
const [protAction, selectProtAction] = useState('protect');
|
||||
const [date, selectDate] = useState(maxDate);
|
||||
|
@ -159,9 +218,12 @@ function Modtools() {
|
|||
const [brcoords, selectBRCoords] = useState(keptState.brcoords);
|
||||
const [tlrcoords, selectTLRCoords] = useState(keptState.tlrcoords);
|
||||
const [brrcoords, selectBRRCoords] = useState(keptState.brrcoords);
|
||||
const [modName, selectModName] = useState(null);
|
||||
const [tlccoords, selectTLCCoords] = useState(keptState.tlrcoords);
|
||||
const [brccoords, selectBRCCoords] = useState(keptState.brrcoords);
|
||||
const [modName, selectModName] = useState('');
|
||||
const [resp, setResp] = useState(null);
|
||||
const [modlist, setModList] = useState([]);
|
||||
const [cleanerstats, setCleanerStats] = useState({});
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
|
||||
const [
|
||||
|
@ -193,12 +255,40 @@ function Modtools() {
|
|||
// nothing
|
||||
}
|
||||
|
||||
let descCleanAction;
|
||||
switch (cleanAction) {
|
||||
case 'spare':
|
||||
// eslint-disable-next-line max-len
|
||||
descCleanAction = t`Clean spare pixels that are surrounded by unset pixels`;
|
||||
break;
|
||||
case 'spareext':
|
||||
// eslint-disable-next-line max-len
|
||||
descCleanAction = t`Clean spare pixels that are surrounded by a single other color`;
|
||||
break;
|
||||
default:
|
||||
// nothing
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (userlvl === 1) {
|
||||
getModList((mods) => setModList(mods));
|
||||
}
|
||||
if (userlvl > 0) {
|
||||
getCleanerStats((stats) => setCleanerStats(stats));
|
||||
}
|
||||
}, []);
|
||||
|
||||
useInterval(() => {
|
||||
if (userlvl > 0) {
|
||||
getCleanerStats((stats) => setCleanerStats(stats));
|
||||
}
|
||||
}, 10000);
|
||||
|
||||
const cleanerStatusString = (!cleanerstats.running)
|
||||
? t`Status: Not running`
|
||||
// eslint-disable-next-line max-len
|
||||
: `Status: ${cleanerstats.method} from ${cleanerstats.tl} to ${cleanerstats.br} on canvas ${canvases[cleanerstats.canvasId].ident} to ${cleanerstats.percent} done`;
|
||||
|
||||
return (
|
||||
<div style={{ textAlign: 'center', paddingLeft: '5%', paddingRight: '5%' }}>
|
||||
{resp && (
|
||||
|
@ -227,6 +317,7 @@ function Modtools() {
|
|||
)}
|
||||
<p className="modalcotext">Choose Canvas:
|
||||
<select
|
||||
value={selectedCanvas}
|
||||
onChange={(e) => {
|
||||
const sel = e.target;
|
||||
selectCanvas(sel.options[sel.selectedIndex].value);
|
||||
|
@ -237,7 +328,6 @@ function Modtools() {
|
|||
? null
|
||||
: (
|
||||
<option
|
||||
selected={canvas === selectedCanvas}
|
||||
value={canvas}
|
||||
>
|
||||
{
|
||||
|
@ -257,6 +347,7 @@ function Modtools() {
|
|||
<input type="file" name="image" id="imgfile" />
|
||||
</p>
|
||||
<select
|
||||
value={imageAction}
|
||||
onChange={(e) => {
|
||||
const sel = e.target;
|
||||
selectImageAction(sel.options[sel.selectedIndex].value);
|
||||
|
@ -265,7 +356,6 @@ function Modtools() {
|
|||
{['build', 'protect', 'wipe'].map((opt) => (
|
||||
<option
|
||||
value={opt}
|
||||
selected={imageAction === opt}
|
||||
>
|
||||
{opt}
|
||||
</option>
|
||||
|
@ -320,6 +410,7 @@ function Modtools() {
|
|||
use protect with image upload and alpha layers)`}
|
||||
</p>
|
||||
<select
|
||||
value={protAction}
|
||||
onChange={(e) => {
|
||||
const sel = e.target;
|
||||
selectProtAction(sel.options[sel.selectedIndex].value);
|
||||
|
@ -328,14 +419,13 @@ function Modtools() {
|
|||
{['protect', 'unprotect'].map((opt) => (
|
||||
<option
|
||||
value={opt}
|
||||
selected={protAction === opt}
|
||||
>
|
||||
{opt}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<p className="modalcotext">
|
||||
Top-left corner (X_Y):
|
||||
{t`Top-left corner`} (X_Y):
|
||||
<input
|
||||
value={tlcoords}
|
||||
style={{
|
||||
|
@ -353,7 +443,7 @@ function Modtools() {
|
|||
/>
|
||||
</p>
|
||||
<p className="modalcotext">
|
||||
Bottom-right corner (X_Y):
|
||||
{t`Bottom-right corner`} (X_Y):
|
||||
<input
|
||||
value={brcoords}
|
||||
style={{
|
||||
|
@ -410,7 +500,7 @@ function Modtools() {
|
|||
}}
|
||||
/>
|
||||
<p className="modalcotext">
|
||||
Top-left corner (X_Y):
|
||||
{t`Top-left corner`} (X_Y):
|
||||
<input
|
||||
value={tlrcoords}
|
||||
style={{
|
||||
|
@ -428,7 +518,7 @@ function Modtools() {
|
|||
/>
|
||||
</p>
|
||||
<p className="modalcotext">
|
||||
Bottom-right corner (X_Y):
|
||||
{t`Bottom-right corner`} (X_Y):
|
||||
<input
|
||||
value={brrcoords}
|
||||
style={{
|
||||
|
@ -468,6 +558,115 @@ function Modtools() {
|
|||
</button>
|
||||
</div>
|
||||
)}
|
||||
<br />
|
||||
<div className="modaldivider" />
|
||||
<h3 className="modaltitle">{t`Canvas Cleaner`}</h3>
|
||||
<p className="modalcotext">
|
||||
{t`Apply a filter to clean trash in large canvas areas.`}
|
||||
</p>
|
||||
<select
|
||||
value={cleanAction}
|
||||
onChange={(e) => {
|
||||
const sel = e.target;
|
||||
selectCleanAction(sel.options[sel.selectedIndex].value);
|
||||
}}
|
||||
>
|
||||
{['spare', 'spareext'].map((opt) => (
|
||||
<option
|
||||
value={opt}
|
||||
>
|
||||
{opt}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<p className="modalcotext">{descCleanAction}</p>
|
||||
<p className="modalcotext" style={{ fontWeight: 'bold' }}>
|
||||
{cleanerStatusString}
|
||||
</p>
|
||||
<p className="modalcotext">
|
||||
{t`Top-left corner`} (X_Y):
|
||||
<input
|
||||
value={tlccoords}
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
width: '100%',
|
||||
maxWidth: '15em',
|
||||
}}
|
||||
type="text"
|
||||
placeholder="X_Y"
|
||||
onChange={(evt) => {
|
||||
const co = evt.target.value.trim();
|
||||
selectTLCCoords(co);
|
||||
keptState.tlccoords = co;
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
<p className="modalcotext">
|
||||
{t`Bottom-right corner`} (X_Y):
|
||||
<input
|
||||
value={brccoords}
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
width: '100%',
|
||||
maxWidth: '15em',
|
||||
}}
|
||||
type="text"
|
||||
placeholder="X_Y"
|
||||
onChange={(evt) => {
|
||||
const co = evt.target.value.trim();
|
||||
selectBRCCoords(co);
|
||||
keptState.brccoords = co;
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (submitting) {
|
||||
return;
|
||||
}
|
||||
setSubmitting(true);
|
||||
submitCanvasCleaner(
|
||||
cleanAction,
|
||||
selectedCanvas,
|
||||
tlccoords,
|
||||
brccoords,
|
||||
(ret) => {
|
||||
setCleanerStats({
|
||||
running: true,
|
||||
percent: 'N/A',
|
||||
method: cleanAction,
|
||||
tl: tlccoords,
|
||||
br: brccoords,
|
||||
canvasId: selectedCanvas,
|
||||
});
|
||||
setSubmitting(false);
|
||||
setResp(ret);
|
||||
},
|
||||
);
|
||||
}}
|
||||
>
|
||||
{(submitting) ? '...' : t`Submit`}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (submitting) {
|
||||
return;
|
||||
}
|
||||
setSubmitting(true);
|
||||
getCleanerCancel(
|
||||
(ret) => {
|
||||
setCleanerStats({});
|
||||
setSubmitting(false);
|
||||
setResp(ret);
|
||||
},
|
||||
);
|
||||
}}
|
||||
>
|
||||
{(submitting) ? '...' : t`Stop Cleaner`}
|
||||
</button>
|
||||
|
||||
{(userlvl === 1) && (
|
||||
<div>
|
||||
<br />
|
||||
|
@ -477,6 +676,7 @@ function Modtools() {
|
|||
{t`Do stuff with IPs (one IP per line)`}
|
||||
</p>
|
||||
<select
|
||||
value={iPAction}
|
||||
onChange={(e) => {
|
||||
const sel = e.target;
|
||||
selectIPAction(sel.options[sel.selectedIndex].value);
|
||||
|
@ -485,7 +685,6 @@ function Modtools() {
|
|||
{['ban', 'unban', 'whitelist', 'unwhitelist'].map((opt) => (
|
||||
<option
|
||||
value={opt}
|
||||
selected={iPAction === opt}
|
||||
>
|
||||
{opt}
|
||||
</option>
|
||||
|
|
24
src/components/hooks/useInterval.js
Normal file
24
src/components/hooks/useInterval.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { useEffect, useRef } from 'react';
|
||||
|
||||
function useInterval(callback, delay) {
|
||||
const savedCallback = useRef();
|
||||
|
||||
// Remember the latest callback.
|
||||
useEffect(() => {
|
||||
savedCallback.current = callback;
|
||||
}, [callback]);
|
||||
|
||||
// Set up the interval.
|
||||
// eslint-disable-next-line consistent-return
|
||||
useEffect(() => {
|
||||
function tick() {
|
||||
savedCallback.current();
|
||||
}
|
||||
if (delay !== null) {
|
||||
const id = setInterval(tick, delay);
|
||||
return () => clearInterval(id);
|
||||
}
|
||||
}, [delay]);
|
||||
}
|
||||
|
||||
export default useInterval;
|
|
@ -80,7 +80,7 @@ class PixelPlainterControls {
|
|||
viewport.addEventListener('mousemove', this.onMouseMove, false);
|
||||
viewport.addEventListener('mouseup', this.onMouseUp, false);
|
||||
// TODO check if we can go passive here
|
||||
//viewport.addEventListener('wheel', this.onWheel, { passive: true });
|
||||
// viewport.addEventListener('wheel', this.onWheel, { passive: true });
|
||||
viewport.addEventListener('wheel', this.onWheel, false);
|
||||
viewport.addEventListener('touchstart', this.onTouchStart, false);
|
||||
viewport.addEventListener('touchend', this.onTouchEnd, false);
|
||||
|
|
406
src/core/CanvasCleaner.js
Normal file
406
src/core/CanvasCleaner.js
Normal file
|
@ -0,0 +1,406 @@
|
|||
/*
|
||||
* runs a filter over a larger canvas area over time
|
||||
*
|
||||
*/
|
||||
|
||||
import {
|
||||
setData,
|
||||
getData,
|
||||
setStatus,
|
||||
getStatus,
|
||||
} from '../data/models/CanvasCleaner';
|
||||
import RedisCanvas from '../data/models/RedisCanvas';
|
||||
import {
|
||||
getChunkOfPixel,
|
||||
getCornerOfChunk,
|
||||
} from './utils';
|
||||
import { setPixelByOffset } from './setPixel';
|
||||
import {
|
||||
TILE_SIZE,
|
||||
} from './constants';
|
||||
import logger from './logger';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import canvases from './canvases.json';
|
||||
|
||||
const METHODS = {
|
||||
/*
|
||||
* @param xc, yc chunk coordinates of pixel relative to center chunk
|
||||
* of chunk area
|
||||
*/
|
||||
spare: (xc, yc, clrIgnore, canvasCleaner) => {
|
||||
let rplPxl = null;
|
||||
for (let u = -1; u <= 1; u += 1) {
|
||||
for (let v = -1; v <= 1; v += 1) {
|
||||
const pxl = canvasCleaner.getPixelInChunkArea(xc + u, yc + v);
|
||||
if (pxl === null) {
|
||||
continue;
|
||||
}
|
||||
if (u === 0 && v === 0) {
|
||||
if (pxl < clrIgnore) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
if (pxl >= clrIgnore) {
|
||||
return null;
|
||||
}
|
||||
if (rplPxl === null) {
|
||||
rplPxl = pxl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return rplPxl;
|
||||
},
|
||||
|
||||
spareext: (xc, yc, clrIgnore, canvasCleaner) => {
|
||||
let rplPxl = null;
|
||||
let origPxl = null;
|
||||
for (let u = -1; u <= 1; u += 1) {
|
||||
for (let v = -1; v <= 1; v += 1) {
|
||||
const pxl = canvasCleaner.getPixelInChunkArea(xc + u, yc + v);
|
||||
if (pxl === null) {
|
||||
continue;
|
||||
}
|
||||
if (u === 0 && v === 0) {
|
||||
if (pxl < clrIgnore || pxl === rplPxl) {
|
||||
return null;
|
||||
}
|
||||
origPxl = pxl;
|
||||
} else {
|
||||
if (rplPxl === null) {
|
||||
rplPxl = pxl;
|
||||
}
|
||||
if (pxl >= clrIgnore) {
|
||||
if (rplPxl < clrIgnore) {
|
||||
rplPxl = pxl;
|
||||
continue;
|
||||
}
|
||||
if (pxl !== rplPxl) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rplPxl === origPxl) {
|
||||
return null;
|
||||
}
|
||||
return rplPxl;
|
||||
},
|
||||
};
|
||||
|
||||
class CanvasCleaner {
|
||||
// canvas id: integer
|
||||
canvasId;
|
||||
// coords of top left and bottom right corner of area: integer
|
||||
x;
|
||||
y;
|
||||
u;
|
||||
v;
|
||||
// name of filter method, string
|
||||
methodName;
|
||||
// 3x3 canvas area
|
||||
// [
|
||||
// [AA, AB, AC],
|
||||
// [BA, BB, BC],
|
||||
// [CA, CB, CC],
|
||||
// ]
|
||||
chunks;
|
||||
// chunk coordinates of center BB of chunks
|
||||
centerChunk;
|
||||
// iterator over chunks
|
||||
cIter;
|
||||
// info about chunks of total affected area
|
||||
// cx, cy: top right chunk coords
|
||||
// cw, ch: height and width in chunks
|
||||
// amountChunks: cw * ch
|
||||
cx; cy;
|
||||
cw; ch;
|
||||
amountChunks;
|
||||
// current setTimeout index
|
||||
tick;
|
||||
// if running: boolean
|
||||
running;
|
||||
// stats
|
||||
pxlProcessed;
|
||||
pxlCleaned;
|
||||
|
||||
constructor() {
|
||||
this.logger = (text) => {
|
||||
logger.warn(`[CanvasCleaner] ${text}`);
|
||||
};
|
||||
this.cleanChunk = this.cleanChunk.bind(this);
|
||||
this.clearValues();
|
||||
this.loadArgs();
|
||||
}
|
||||
|
||||
clearValues() {
|
||||
this.running = false;
|
||||
this.chunks = [
|
||||
[null, null, null],
|
||||
[null, null, null],
|
||||
[null, null, null],
|
||||
];
|
||||
this.centerChunk = [null, null];
|
||||
this.cIter = 0;
|
||||
this.cx = 0;
|
||||
this.cy = 0;
|
||||
this.cw = 0;
|
||||
this.ch = 0;
|
||||
this.amountChunks = 0;
|
||||
this.pxlProcessed = 0;
|
||||
this.pxlCleaned = 0;
|
||||
this.tick = null;
|
||||
}
|
||||
|
||||
async loadArgs() {
|
||||
const [cIter, running] = await getStatus();
|
||||
if (running) {
|
||||
const [canvasId, x, y, u, v, methodName] = await getData();
|
||||
this.set(canvasId, x, y, u, v, methodName, cIter);
|
||||
}
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.running = false;
|
||||
const str = 'Stopped CanvasCleaner';
|
||||
this.logger(str);
|
||||
return str;
|
||||
}
|
||||
|
||||
async cleanChunk() {
|
||||
this.tick = null;
|
||||
const {
|
||||
canvasId, cIter, cw, cx, cy,
|
||||
} = this;
|
||||
const method = METHODS[this.methodName];
|
||||
if (cIter >= this.amountChunks || !this.running) {
|
||||
// finished
|
||||
// eslint-disable-next-line max-len
|
||||
this.logger(`Finished Cleaning on ${this.x},${this.y}, cleaned ${this.pxlCleaned} / ${this.pxlProcessed} pixels`);
|
||||
this.clearValues();
|
||||
this.saveStatus();
|
||||
return;
|
||||
}
|
||||
const canvas = canvases[canvasId];
|
||||
let i = (cIter % cw);
|
||||
const j = ((cIter - i) / cw) + cy;
|
||||
i += cx;
|
||||
const clrIgnore = canvas.cli || 0;
|
||||
|
||||
await this.loadChunkArea(i, j);
|
||||
if (this.checkIfChunkInArea(i, j)) {
|
||||
const [xCor, yCor] = getCornerOfChunk(canvas.size, i, j);
|
||||
const xLow = (xCor > this.x) ? 0 : (this.x - xCor);
|
||||
const yLow = (yCor > this.y) ? 0 : (this.y - yCor);
|
||||
const xHigh = (xCor + TILE_SIZE <= this.u) ? TILE_SIZE
|
||||
: (this.u - xCor + 1);
|
||||
const yHigh = (yCor + TILE_SIZE <= this.v) ? TILE_SIZE
|
||||
: (this.v - yCor + 1);
|
||||
for (let xc = xLow; xc < xHigh; xc += 1) {
|
||||
for (let yc = yLow; yc < yHigh; yc += 1) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const rplPxl = await method(xc, yc, clrIgnore, this);
|
||||
this.pxlProcessed += 1;
|
||||
if (rplPxl !== null) {
|
||||
this.pxlCleaned += 1;
|
||||
setPixelByOffset(
|
||||
canvasId,
|
||||
rplPxl,
|
||||
i, j,
|
||||
yc * TILE_SIZE + xc,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.saveStatus();
|
||||
this.cIter += 1;
|
||||
|
||||
this.tick = setTimeout(this.cleanChunk, 500);
|
||||
}
|
||||
|
||||
set(canvasId, x, y, u, v, methodName, cIter = 0) {
|
||||
if (!METHODS[methodName]) {
|
||||
const str = `Method ${methodName} not available`;
|
||||
this.logger(str);
|
||||
return str;
|
||||
}
|
||||
const canvas = canvases[canvasId];
|
||||
if (!canvas) {
|
||||
const str = `Canvas ${canvasId} invalid`;
|
||||
this.logger(str);
|
||||
return str;
|
||||
}
|
||||
if (canvas.v) {
|
||||
const str = 'Can not clean 3D canvas';
|
||||
this.logger(str);
|
||||
return str;
|
||||
}
|
||||
if (x > u || y > v) {
|
||||
const str = 'Invalid area';
|
||||
this.logger(str);
|
||||
return str;
|
||||
}
|
||||
const canvasSize = canvas.size;
|
||||
const canvasMaxXY = canvasSize / 2;
|
||||
const canvasMinXY = -canvasMaxXY;
|
||||
if (x < canvasMinXY || y < canvasMinXY
|
||||
|| x >= canvasMaxXY || y >= canvasMaxXY
|
||||
|| u < canvasMinXY || v < canvasMinXY
|
||||
|| u >= canvasMaxXY || v >= canvasMaxXY) {
|
||||
const str = 'Coordinates out of bounds';
|
||||
this.logger(str);
|
||||
return str;
|
||||
}
|
||||
|
||||
if (this.tick) {
|
||||
this.running = false;
|
||||
clearTimeout(this.tick);
|
||||
}
|
||||
this.canvasId = canvasId;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.u = u;
|
||||
this.v = v;
|
||||
this.cIter = cIter;
|
||||
this.methodName = methodName;
|
||||
const [cx, cy] = getChunkOfPixel(canvas.size, this.x, this.y);
|
||||
this.cx = cx;
|
||||
this.cy = cy;
|
||||
const [cu, cv] = getChunkOfPixel(canvas.size, this.u, this.v);
|
||||
this.cw = cu - cx + 1;
|
||||
this.ch = cv - cy + 1;
|
||||
this.amountChunks = this.cw * this.ch;
|
||||
|
||||
this.running = true;
|
||||
this.tick = setTimeout(this.cleanChunk, 500);
|
||||
// eslint-disable-next-line max-len
|
||||
this.logger(`Start Cleaning on #${canvas.ident},${this.x},${this.y} till #${canvas.ident},${this.u},${this.v} with method ${methodName}`);
|
||||
this.saveData();
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* get pixel out of 3x3 chunk area
|
||||
* @param x, y coordinates relative to center chunk
|
||||
* @return integer color index or null if chunk is empty
|
||||
*/
|
||||
getPixelInChunkArea(x, y) {
|
||||
const { chunks } = this;
|
||||
let col;
|
||||
let xc = x;
|
||||
if (x >= 0 && x < TILE_SIZE) {
|
||||
col = 1;
|
||||
} else if (x < 0) {
|
||||
col = 0;
|
||||
xc += TILE_SIZE;
|
||||
} else {
|
||||
col = 2;
|
||||
xc -= TILE_SIZE;
|
||||
}
|
||||
let row;
|
||||
let yc = y;
|
||||
if (y >= 0 && y < TILE_SIZE) {
|
||||
row = 1;
|
||||
} else if (y < 0) {
|
||||
row = 0;
|
||||
yc += TILE_SIZE;
|
||||
} else {
|
||||
row = 2;
|
||||
yc -= TILE_SIZE;
|
||||
}
|
||||
const chunk = chunks[row][col];
|
||||
if (!chunk) return null;
|
||||
// get rid of protection
|
||||
return chunk[yc * TILE_SIZE + xc] & 0x3F;
|
||||
}
|
||||
|
||||
/*
|
||||
* load 3x3 chunk area
|
||||
* @param i, j chunk coordinates of center chunk
|
||||
*/
|
||||
async loadChunkArea(i, j) {
|
||||
const { chunks, centerChunk, canvasId } = this;
|
||||
const [io, jo] = centerChunk;
|
||||
const newChunks = [
|
||||
[null, null, null],
|
||||
[null, null, null],
|
||||
[null, null, null],
|
||||
];
|
||||
for (let iRel = -1; iRel <= 1; iRel += 1) {
|
||||
for (let jRel = -1; jRel <= 1; jRel += 1) {
|
||||
let chunk = null;
|
||||
const iAbs = iRel + i;
|
||||
const jAbs = jRel + j;
|
||||
if (
|
||||
io && jo
|
||||
&& iAbs >= io - 1
|
||||
&& iAbs <= io + 1
|
||||
&& jAbs >= jo - 1
|
||||
&& jAbs <= jo + 1
|
||||
) {
|
||||
chunk = chunks[jAbs - jo + 1][iAbs - io + 1];
|
||||
} else {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
chunk = await RedisCanvas.getChunk(canvasId, iAbs, jAbs);
|
||||
if (!chunk || chunk.length !== TILE_SIZE * TILE_SIZE) {
|
||||
chunk = null;
|
||||
if (chunk) {
|
||||
// eslint-disable-next-line max-len
|
||||
this.logger(`Chunk ch:${canvasId}:${iAbs}:${jAbs} has invalid size ${chunk.length}.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
newChunks[jRel + 1][iRel + 1] = chunk;
|
||||
}
|
||||
}
|
||||
this.chunks = newChunks;
|
||||
this.centerChunk = [i, j];
|
||||
}
|
||||
|
||||
/*
|
||||
* check if chunk exists in area and is not empty
|
||||
* @param i, j chunk to check
|
||||
*/
|
||||
checkIfChunkInArea(i, j) {
|
||||
const { chunks, centerChunk } = this;
|
||||
const [io, jo] = centerChunk;
|
||||
if (
|
||||
io && jo
|
||||
&& i >= io - 1
|
||||
&& i <= io + 1
|
||||
&& j >= jo - 1
|
||||
&& j <= jo + 1
|
||||
) {
|
||||
const col = i - io + 1;
|
||||
const row = j - jo + 1;
|
||||
if (chunks[row][col] !== null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
reportStatus() {
|
||||
return {
|
||||
running: this.running,
|
||||
canvasId: this.canvasId,
|
||||
percent: `${this.cIter} / ${this.amountChunks}`,
|
||||
tl: `${this.x}_${this.y}`,
|
||||
br: `${this.u}_${this.v}`,
|
||||
method: this.methodName,
|
||||
};
|
||||
}
|
||||
|
||||
saveData() {
|
||||
setData(this.canvasId, this.x, this.y, this.u, this.v, this.methodName);
|
||||
}
|
||||
|
||||
saveStatus() {
|
||||
setStatus(this.cIter, this.running);
|
||||
}
|
||||
}
|
||||
|
||||
export default new CanvasCleaner();
|
|
@ -1,7 +1,6 @@
|
|||
/*
|
||||
* functions for admintools
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
/* eslint-disable no-await-in-loop */
|
||||
|
@ -10,8 +9,9 @@ import sharp from 'sharp';
|
|||
import Sequelize from 'sequelize';
|
||||
import redis from '../data/redis';
|
||||
|
||||
import { modtoolsLogger } from './logger';
|
||||
import { getIPv6Subnet } from '../utils/ip';
|
||||
import { validateCoorRange } from '../utils/validation';
|
||||
import CanvasCleaner from './CanvasCleaner';
|
||||
import { Blacklist, Whitelist, RegUser } from '../data/models';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import canvases from './canvases.json';
|
||||
|
@ -27,7 +27,7 @@ import rollbackCanvasArea from './rollback';
|
|||
* @param ip already sanizized ip
|
||||
* @return true if successful
|
||||
*/
|
||||
export async function executeIPAction(action: string, ips: string): string {
|
||||
export async function executeIPAction(action, ips, logger = null) {
|
||||
const ipArray = ips.split('\n');
|
||||
let out = '';
|
||||
const splitRegExp = /\s+/;
|
||||
|
@ -46,7 +46,7 @@ export async function executeIPAction(action: string, ips: string): string {
|
|||
const ipKey = getIPv6Subnet(ip);
|
||||
const key = `isprox:${ipKey}`;
|
||||
|
||||
modtoolsLogger.info(`ADMINTOOLS: ${action} ${ip}`);
|
||||
if (logger) logger(`${action} ${ip}`);
|
||||
switch (action) {
|
||||
case 'ban':
|
||||
await Blacklist.findOrCreate({
|
||||
|
@ -89,10 +89,11 @@ export async function executeIPAction(action: string, ips: string): string {
|
|||
* @return [ret, msg] http status code and message
|
||||
*/
|
||||
export async function executeImageAction(
|
||||
action: string,
|
||||
file: Object,
|
||||
coords: string,
|
||||
canvasid: string,
|
||||
action,
|
||||
file,
|
||||
coords,
|
||||
canvasid,
|
||||
logger = null,
|
||||
) {
|
||||
if (!coords) {
|
||||
return [403, 'Coordinates not defined'];
|
||||
|
@ -150,7 +151,7 @@ export async function executeImageAction(
|
|||
);
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
modtoolsLogger.info(`ADMINTOOLS: Loaded image wth ${pxlCount} pixels to ${x}/${y}`);
|
||||
if (logger) logger(`Loaded image wth ${pxlCount} pixels to #${canvas.ident},${x},${y}`);
|
||||
return [
|
||||
200,
|
||||
`Successfully loaded image wth ${pxlCount} pixels to ${x}/${y}`,
|
||||
|
@ -160,6 +161,53 @@ export async function executeImageAction(
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute actions for cleaning/filtering canvas
|
||||
* @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 executeCleanerAction(
|
||||
action,
|
||||
ulcoor,
|
||||
brcoor,
|
||||
canvasid,
|
||||
logger = null,
|
||||
) {
|
||||
if (!canvasid) {
|
||||
return [403, 'canvasid not defined'];
|
||||
}
|
||||
const canvas = canvases[canvasid];
|
||||
let error = null;
|
||||
if (!ulcoor || !brcoor) {
|
||||
error = 'Not all coordinates defined';
|
||||
} else if (!canvas) {
|
||||
error = 'Invalid canvas selected';
|
||||
} else if (!action) {
|
||||
error = 'No cleanaction given';
|
||||
}
|
||||
if (error) {
|
||||
return [403, error];
|
||||
}
|
||||
|
||||
const parseCoords = validateCoorRange(ulcoor, brcoor, canvas.size);
|
||||
if (typeof parseCoords === 'string') {
|
||||
return [403, parseCoords];
|
||||
}
|
||||
const [x, y, u, v] = parseCoords;
|
||||
|
||||
error = CanvasCleaner.set(canvasid, x, y, u, v, action);
|
||||
if (error) {
|
||||
return [403, error];
|
||||
}
|
||||
// eslint-disable-next-line max-len
|
||||
const report = `Set Canvas Cleaner to ${action} canvas ${canvas.ident} from ${ulcoor} to ${brcoor}`;
|
||||
if (logger) logger(report);
|
||||
return [200, report];
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute actions for protecting areas
|
||||
* @param action what to do
|
||||
|
@ -169,46 +217,23 @@ export async function executeImageAction(
|
|||
* @return [ret, msg] http status code and message
|
||||
*/
|
||||
export async function executeProtAction(
|
||||
action: string,
|
||||
ulcoor: string,
|
||||
brcoor: string,
|
||||
canvasid: number,
|
||||
action,
|
||||
ulcoor,
|
||||
brcoor,
|
||||
canvasid,
|
||||
logger = null,
|
||||
) {
|
||||
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';
|
||||
if (!ulcoor || !brcoor) {
|
||||
error = 'Not all coordinates defined';
|
||||
} else if (!canvas) {
|
||||
error = 'Invalid canvas selected';
|
||||
} else if (!action) {
|
||||
error = 'No imageaction given';
|
||||
} else if (action !== 'protect' && action !== 'unprotect') {
|
||||
error = 'Invalid action (must be protect or unprotect)';
|
||||
}
|
||||
|
@ -216,19 +241,17 @@ export async function executeProtAction(
|
|||
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 parseCoords = validateCoorRange(ulcoor, brcoor, canvas.size);
|
||||
if (typeof parseCoords === 'string') {
|
||||
return [403, parseCoords];
|
||||
}
|
||||
const [x, y, u, v] = parseCoords;
|
||||
|
||||
const width = u - x + 1;
|
||||
const height = v - y + 1;
|
||||
if (width * height > 10000000) {
|
||||
return [403, 'Can not set protection to more than 10m pixels at onec'];
|
||||
}
|
||||
const protect = action === 'protect';
|
||||
const pxlCount = await protectCanvasArea(
|
||||
canvasid,
|
||||
|
@ -238,10 +261,13 @@ export async function executeProtAction(
|
|||
height,
|
||||
protect,
|
||||
);
|
||||
modtoolsLogger.info(
|
||||
// eslint-disable-next-line max-len
|
||||
`ADMINTOOLS: Set protect to ${protect} for ${pxlCount} pixels at ${x} / ${y} with dimension ${width}x${height}`,
|
||||
if (logger) {
|
||||
logger(
|
||||
(protect)
|
||||
? `Protect ${width}x${height} area at #${canvas.ident},${x},${y}`
|
||||
: `Unprotect ${width}x${height} area at #${canvas.ident},${x},${y}`,
|
||||
);
|
||||
}
|
||||
return [
|
||||
200,
|
||||
(protect)
|
||||
|
@ -261,63 +287,36 @@ export async function executeProtAction(
|
|||
* @return [ret, msg] http status code and message
|
||||
*/
|
||||
export async function executeRollback(
|
||||
date: string,
|
||||
ulcoor: string,
|
||||
brcoor: string,
|
||||
canvasid: number,
|
||||
date,
|
||||
ulcoor,
|
||||
brcoor,
|
||||
canvasid,
|
||||
logger = null,
|
||||
) {
|
||||
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';
|
||||
if (!ulcoor || !brcoor) {
|
||||
error = 'Not all coordinates defined';
|
||||
} else if (!canvas) {
|
||||
error = 'Invalid canvas selected';
|
||||
} else if (!date) {
|
||||
error = 'No date given';
|
||||
} else if (Number.isNaN(Number(date)) || date.length !== 8) {
|
||||
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 parseCoords = validateCoorRange(ulcoor, brcoor, canvas.size);
|
||||
if (typeof parseCoords === 'string') {
|
||||
return [403, parseCoords];
|
||||
}
|
||||
const [x, y, u, v] = parseCoords;
|
||||
|
||||
const width = u - x + 1;
|
||||
const height = v - y + 1;
|
||||
|
@ -333,10 +332,12 @@ export async function executeRollback(
|
|||
height,
|
||||
date,
|
||||
);
|
||||
modtoolsLogger.info(
|
||||
if (logger) {
|
||||
logger(
|
||||
// eslint-disable-next-line max-len
|
||||
`ADMINTOOLS: Rollback to ${date} for ${pxlCount} pixels at ${x} / ${y} with dimension ${width}x${height}`,
|
||||
`Rollback to ${date} for ${pxlCount} pixels with dimension ${width}x${height} at #${canvas.ident},${x},${y}`,
|
||||
);
|
||||
}
|
||||
return [
|
||||
200,
|
||||
// eslint-disable-next-line max-len
|
||||
|
@ -381,6 +382,9 @@ export async function removeMod(userId) {
|
|||
}
|
||||
|
||||
export async function makeMod(name) {
|
||||
if (!name) {
|
||||
throw new Error('No username given');
|
||||
}
|
||||
let user = null;
|
||||
try {
|
||||
user = await RegUser.findOne({
|
||||
|
|
|
@ -77,6 +77,19 @@ export function getChunkOfPixel(
|
|||
return [cx, cy];
|
||||
}
|
||||
|
||||
// get coordinates of top-left corner of chunk
|
||||
export function getCornerOfChunk(
|
||||
canvasSize,
|
||||
i,
|
||||
j,
|
||||
is3d = false,
|
||||
) {
|
||||
const tileSize = (is3d) ? THREE_TILE_SIZE : TILE_SIZE;
|
||||
const x = (i * tileSize) - (canvasSize / 2);
|
||||
const y = (j * tileSize) - (canvasSize / 2);
|
||||
return [x, y, 0];
|
||||
}
|
||||
|
||||
export function getTileOfPixel(
|
||||
tileScale,
|
||||
pixel,
|
||||
|
|
107
src/data/models/CanvasCleaner.js
Normal file
107
src/data/models/CanvasCleaner.js
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* storing Event data
|
||||
*/
|
||||
|
||||
import redis from '../redis';
|
||||
import logger from '../../core/logger';
|
||||
|
||||
const DATA_KEY = 'clr:dat';
|
||||
const STAT_KEY = 'clr:sta';
|
||||
|
||||
/*
|
||||
* Gets data of CanvasCleaner from redis
|
||||
* @return Array with [canvasId, x, y, u, v, methodName] (all int except Name)
|
||||
* (check core/CanvasCleaner for the meaning)
|
||||
*/
|
||||
export async function getData() {
|
||||
const data = await redis.getAsync(DATA_KEY);
|
||||
if (data) {
|
||||
const parsedData = data.toString().split(':');
|
||||
for (let i = 0; i < parsedData.length - 1; i += 1) {
|
||||
const num = parseInt(parsedData[i], 10);
|
||||
if (Number.isNaN(num)) {
|
||||
logger.warn(
|
||||
// eslint-disable-next-line max-len
|
||||
`[CanvasCleaner] ${DATA_KEY} in redis does not seem legit (int conversion).`,
|
||||
);
|
||||
return [0, 0, 0, 0, 0, 0, ''];
|
||||
}
|
||||
parsedData[i] = num;
|
||||
}
|
||||
if (parsedData.length === 6) {
|
||||
return parsedData;
|
||||
}
|
||||
logger.warn(
|
||||
`[CanvasCleaner] ${DATA_KEY} in redis does not seem legit.`,
|
||||
);
|
||||
}
|
||||
return [0, 0, 0, 0, 0, 0, ''];
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes data of CanvasCleaner to redis
|
||||
* @param check out core/CanvasCleaner
|
||||
*/
|
||||
export async function setData(canvasId, x, y, u, v, methodName) {
|
||||
const dataStr = `${canvasId}:${x}:${y}:${u}:${v}:${methodName}`;
|
||||
if (
|
||||
Number.isNaN(parseInt(canvasId, 10))
|
||||
|| Number.isNaN(parseInt(x, 10))
|
||||
|| Number.isNaN(parseInt(y, 10))
|
||||
|| Number.isNaN(parseInt(u, 10))
|
||||
|| Number.isNaN(parseInt(v, 10))
|
||||
) {
|
||||
logger.warn(
|
||||
`[CanvasCleaner] can not write ${dataStr} to redis, seems not legit.`,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
return redis.setAsync(DATA_KEY, dataStr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets status of CanvasCleaner from redis
|
||||
* @return Array with [cIter, running]
|
||||
* cIter: current chunk iterator integer
|
||||
* running: boolean if filter is running
|
||||
*/
|
||||
export async function getStatus() {
|
||||
const stat = await redis.getAsync(STAT_KEY);
|
||||
if (stat) {
|
||||
const parsedStat = stat.toString().split(':');
|
||||
if (parsedStat.length !== 2) {
|
||||
logger.warn(
|
||||
`[CanvasCleaner] ${STAT_KEY} in redis is incomplete.`,
|
||||
);
|
||||
} else {
|
||||
const cIter = parseInt(parsedStat[0], 10);
|
||||
const running = !!parseInt(parsedStat[1], 10);
|
||||
if (!Number.isNaN(cIter)) {
|
||||
return [cIter, running];
|
||||
}
|
||||
logger.warn(
|
||||
`[CanvasCleaner] ${STAT_KEY} in redis does not seem legit.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
return [0, false];
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes status of CanvasCleaner to redis
|
||||
* @param cIter current chunk iterator integer
|
||||
* @param running Boolean if running or not
|
||||
*/
|
||||
export async function setStatus(cIter, running) {
|
||||
const runningInt = (running) ? 1 : 0;
|
||||
const statString = `${cIter}:${runningInt}`;
|
||||
if (
|
||||
Number.isNaN(parseInt(cIter, 10))
|
||||
) {
|
||||
logger.warn(
|
||||
`[CanvasCleaner] can not write ${statString} to redis, seems not legit.`,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
return redis.setAsync(STAT_KEY, statString);
|
||||
}
|
|
@ -2,7 +2,6 @@
|
|||
*
|
||||
* data saving for hourly events
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
// its ok if its slow
|
||||
|
@ -101,7 +100,7 @@ export async function clearOldEvent() {
|
|||
* @param minutes minutes till next event
|
||||
* @param i, j chunk coordinates of center of event
|
||||
*/
|
||||
export async function setNextEvent(minutes: number, i: number, j: number) {
|
||||
export async function setNextEvent(minutes, i, j) {
|
||||
await clearOldEvent();
|
||||
for (let jc = j - 1; jc <= j + 1; jc += 1) {
|
||||
for (let ic = i - 1; ic <= i + 1; ic += 1) {
|
||||
|
|
|
@ -10,13 +10,15 @@ import express from 'express';
|
|||
import type { Request, Response } from 'express';
|
||||
import multer from 'multer';
|
||||
|
||||
import CanvasCleaner from '../../core/CanvasCleaner';
|
||||
import { getIPFromRequest } from '../../utils/ip';
|
||||
import { modtoolsLogger } from '../../core/logger';
|
||||
import logger, { modtoolsLogger } from '../../core/logger';
|
||||
import {
|
||||
executeIPAction,
|
||||
executeImageAction,
|
||||
executeProtAction,
|
||||
executeRollback,
|
||||
executeCleanerAction,
|
||||
getModList,
|
||||
removeMod,
|
||||
makeMod,
|
||||
|
@ -43,7 +45,7 @@ const upload = multer({
|
|||
router.use(async (req, res, next) => {
|
||||
const ip = getIPFromRequest(req);
|
||||
if (!req.user) {
|
||||
modtoolsLogger.info(
|
||||
logger.warn(
|
||||
`MODTOOLS: ${ip} tried to access modtools without login`,
|
||||
);
|
||||
const { t } = req.ttag;
|
||||
|
@ -55,16 +57,13 @@ router.use(async (req, res, next) => {
|
|||
* 2 = Mod
|
||||
*/
|
||||
if (!req.user.userlvl) {
|
||||
modtoolsLogger.info(
|
||||
logger.warn(
|
||||
`MODTOOLS: ${ip} / ${req.user.id} tried to access modtools`,
|
||||
);
|
||||
const { t } = req.ttag;
|
||||
res.status(403).send(t`You are not allowed to access this page`);
|
||||
return;
|
||||
}
|
||||
modtoolsLogger.info(
|
||||
`MODTOOLS: ${req.user.id} / ${req.user.regUser.name} is using modtools`,
|
||||
);
|
||||
|
||||
next();
|
||||
});
|
||||
|
@ -74,7 +73,40 @@ router.use(async (req, res, next) => {
|
|||
* Post for mod + admin
|
||||
*/
|
||||
router.post('/', upload.single('image'), async (req, res, next) => {
|
||||
const aLogger = (text) => {
|
||||
const timeString = new Date().toLocaleTimeString();
|
||||
modtoolsLogger.info(
|
||||
// eslint-disable-next-line max-len
|
||||
`${timeString} | MODTOOLS> ${req.user.regUser.name}[${req.user.id}]> ${text}`,
|
||||
);
|
||||
};
|
||||
|
||||
try {
|
||||
if (req.body.cleanerstat) {
|
||||
const ret = CanvasCleaner.reportStatus();
|
||||
res.status(200);
|
||||
res.json(ret);
|
||||
return;
|
||||
}
|
||||
if (req.body.cleanercancel) {
|
||||
const ret = CanvasCleaner.stop();
|
||||
res.status(200).send(ret);
|
||||
return;
|
||||
}
|
||||
if (req.body.cleaneraction) {
|
||||
const {
|
||||
cleaneraction, ulcoor, brcoor, canvasid,
|
||||
} = req.body;
|
||||
const [ret, msg] = await executeCleanerAction(
|
||||
cleaneraction,
|
||||
ulcoor,
|
||||
brcoor,
|
||||
canvasid,
|
||||
aLogger,
|
||||
);
|
||||
res.status(ret).send(msg);
|
||||
return;
|
||||
}
|
||||
if (req.body.imageaction) {
|
||||
const { imageaction, coords, canvasid } = req.body;
|
||||
const [ret, msg] = await executeImageAction(
|
||||
|
@ -82,10 +114,12 @@ router.post('/', upload.single('image'), async (req, res, next) => {
|
|||
req.file,
|
||||
coords,
|
||||
canvasid,
|
||||
aLogger,
|
||||
);
|
||||
res.status(ret).send(msg);
|
||||
return;
|
||||
} if (req.body.protaction) {
|
||||
}
|
||||
if (req.body.protaction) {
|
||||
const {
|
||||
protaction, ulcoor, brcoor, canvasid,
|
||||
} = req.body;
|
||||
|
@ -94,10 +128,12 @@ router.post('/', upload.single('image'), async (req, res, next) => {
|
|||
ulcoor,
|
||||
brcoor,
|
||||
canvasid,
|
||||
aLogger,
|
||||
);
|
||||
res.status(ret).send(msg);
|
||||
return;
|
||||
} if (req.body.rollback) {
|
||||
}
|
||||
if (req.body.rollback) {
|
||||
// rollback is date as YYYYMMdd
|
||||
const {
|
||||
rollback, ulcoor, brcoor, canvasid,
|
||||
|
@ -107,6 +143,7 @@ router.post('/', upload.single('image'), async (req, res, next) => {
|
|||
ulcoor,
|
||||
brcoor,
|
||||
canvasid,
|
||||
aLogger,
|
||||
);
|
||||
res.status(ret).send(msg);
|
||||
return;
|
||||
|
@ -135,9 +172,17 @@ router.use(async (req, res, next) => {
|
|||
* Post just for admin
|
||||
*/
|
||||
router.post('/', async (req, res, next) => {
|
||||
const aLogger = (text) => {
|
||||
logger.info(`ADMIN> ${req.user.regUser.name}[${req.user.id}]> ${text}`);
|
||||
};
|
||||
|
||||
try {
|
||||
if (req.body.ipaction) {
|
||||
const ret = await executeIPAction(req.body.ipaction, req.body.ip);
|
||||
const ret = await executeIPAction(
|
||||
req.body.ipaction,
|
||||
req.body.ip,
|
||||
aLogger,
|
||||
);
|
||||
res.status(200).send(ret);
|
||||
return;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user