diff --git a/src/components/Admintools.jsx b/src/components/Admintools.jsx
index 465ae5c..a052fb0 100644
--- a/src/components/Admintools.jsx
+++ b/src/components/Admintools.jsx
@@ -9,8 +9,13 @@ import { connect } from 'react-redux';
import type { State } from '../reducers';
+const keptState = {
+ coords: null,
+ tlcoords: null,
+ brcoords: null,
+};
-async function submitAction(
+async function submitImageAction(
action,
canvas,
coords,
@@ -32,6 +37,26 @@ async function submitAction(
callback(await resp.text());
}
+async function submitProtAction(
+ action,
+ canvas,
+ tlcoords,
+ brcoords,
+ callback,
+) {
+ const data = new FormData();
+ data.append('protaction', action);
+ data.append('canvasid', canvas);
+ data.append('ulcoor', tlcoords);
+ data.append('brcoor', brcoords);
+ const resp = await fetch('./admintools', {
+ credentials: 'include',
+ method: 'POST',
+ body: data,
+ });
+ callback(await resp.text());
+}
+
async function submitIPAction(
action,
callback,
@@ -56,8 +81,11 @@ function Admintools({
const [selectedCanvas, selectCanvas] = useState(canvasId);
const [imageAction, selectImageAction] = useState('build');
const [iPAction, selectIPAction] = useState('ban');
+ const [protAction, selectProtAction] = useState('protect');
+ const [coords, selectCoords] = useState(keptState.coords);
+ const [tlcoords, selectTLCoords] = useState(keptState.tlcoords);
+ const [brcoords, selectBRCoords] = useState(keptState.brcoords);
const [resp, setResp] = useState(null);
- const [coords, selectCoords] = useState('X_Y');
const [submitting, setSubmitting] = useState(false);
let descAction;
@@ -156,8 +184,11 @@ function Admintools({
maxWidth: '15em',
}}
type="text"
+ placeholder="X_Y"
onChange={(evt) => {
- selectCoords(evt.target.value.trim());
+ const co = evt.target.value.trim();
+ selectCoords(co);
+ keptState.coords = co;
}}
/>
@@ -168,7 +199,7 @@ function Admintools({
return;
}
setSubmitting(true);
- submitAction(
+ submitImageAction(
imageAction,
selectedCanvas,
coords,
@@ -181,6 +212,111 @@ function Admintools({
>
{(submitting) ? '...' : 'Submit'}
+
+
+
+ Pixel Protection
+
+ Set protection of areas
+ (if you need finer grained control,
+ use protect with image upload and alpha layers)
+
+ Choose Canvas:
+
+
+
+
+ Top-left corner (X_Y):
+ {
+ const co = evt.target.value.trim();
+ selectTLCoords(co);
+ keptState.tlcoords = co;
+ }}
+ />
+
+
+ Bottom-right corner (X_Y):
+ {
+ const co = evt.target.value.trim();
+ selectBRCoords(co);
+ keptState.brcoords = co;
+ }}
+ />
+
+
+
IP Actions
diff --git a/src/core/Image.js b/src/core/Image.js
index 27de724..79ef920 100644
--- a/src/core/Image.js
+++ b/src/core/Image.js
@@ -200,6 +200,7 @@ export async function protectCanvasArea(
y + height - 1,
);
+ let totalPxlCnt = 0;
let chunk;
for (let cx = ucx; cx <= lcx; cx += 1) {
for (let cy = ucy; cy <= lcy; cy += 1) {
@@ -234,10 +235,12 @@ export async function protectCanvasArea(
if (ret) {
// eslint-disable-next-line max-len
logger.info(`Set protection for ${pxlCnt} pixels in chunk ${cx}, ${cy}.`);
+ totalPxlCnt += pxlCnt;
}
}
chunk = null;
}
}
logger.info('Setting protection for area done.');
+ return totalPxlCnt;
}
diff --git a/src/routes/admintools.js b/src/routes/admintools.js
index f61b50e..775f1d6 100644
--- a/src/routes/admintools.js
+++ b/src/routes/admintools.js
@@ -25,7 +25,10 @@ import { Blacklist, Whitelist } from '../data/models';
import { MINUTE } from '../core/constants';
// eslint-disable-next-line import/no-unresolved
import canvases from './canvases.json';
-import { imageABGR2Canvas } from '../core/Image';
+import {
+ imageABGR2Canvas,
+ protectCanvasArea,
+} from '../core/Image';
const router = express.Router();
@@ -147,7 +150,7 @@ async function executeIPAction(action: string, ips: string): boolean {
* @param action what to do with the image
* @param file imagefile
* @param coords coord sin X_Y format
- * @param canvasid numerical canvas id
+ * @param canvasid numerical canvas id as string
* @return [ret, msg] http status code and message
*/
async function executeImageAction(
@@ -156,12 +159,21 @@ async function executeImageAction(
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';
@@ -169,21 +181,15 @@ async function executeImageAction(
error = 'y is not a valid number';
} else if (!action) {
error = 'No imageaction given';
- } else if (!canvasid) {
- error = 'No canvas specified';
- } else if (!canvases[canvasid]) {
+ } 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 canvas = canvases[canvasid];
-
- if (canvas.v) {
- return [403, 'Can not upload Image to 3D canvas'];
- }
-
const canvasMaxXY = canvas.size / 2;
const canvasMinXY = -canvasMaxXY;
if (x < canvasMinXY || y < canvasMinXY
@@ -208,6 +214,8 @@ async function executeImageAction(
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}`,
@@ -217,6 +225,100 @@ async function executeImageAction(
}
}
+/*
+ * Execute actions for protecting areas
+ * @param action what to do
+ * @param ulcoor coords of upper-left corner in X_Y format
+ * @param brcoord 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 (!canvases[canvasid]) {
+ 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}`,
+ ];
+}
+
/*
* Check for POST parameters,
@@ -237,6 +339,18 @@ router.post('/', upload.single('image'), async (req, res, next) => {
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,
+ } = req.body;
+ const [ret, msg] = await executeProtAction(
+ protaction,
+ ulcoor,
+ brcoor,
+ canvasid,
+ );
+ res.status(ret).send(msg);
+ return;
}
next();