Add parser for current-day pixellog
This commit is contained in:
parent
3c1fb0ead9
commit
86adb162dc
|
@ -49,6 +49,7 @@ async function submitWatchAction(
|
||||||
}
|
}
|
||||||
const data = new FormData();
|
const data = new FormData();
|
||||||
data.append('watchaction', action);
|
data.append('watchaction', action);
|
||||||
|
data.append('canvasid', canvas);
|
||||||
data.append('ulcoor', tlcoords);
|
data.append('ulcoor', tlcoords);
|
||||||
data.append('brcoor', brcoords);
|
data.append('brcoor', brcoords);
|
||||||
data.append('time', time);
|
data.append('time', time);
|
||||||
|
|
|
@ -19,6 +19,10 @@ import {
|
||||||
imageABGR2Canvas,
|
imageABGR2Canvas,
|
||||||
protectCanvasArea,
|
protectCanvasArea,
|
||||||
} from './Image';
|
} from './Image';
|
||||||
|
import {
|
||||||
|
getSummaryFromArea,
|
||||||
|
getPixelsFromArea,
|
||||||
|
} from './parsePixelLog';
|
||||||
import rollbackCanvasArea from './rollback';
|
import rollbackCanvasArea from './rollback';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -165,6 +169,88 @@ export async function executeImageAction(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check who placed on a canvas area
|
||||||
|
* @param action if every pixel or summary should be returned
|
||||||
|
* @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 Object with {info, cols, rows}
|
||||||
|
*/
|
||||||
|
export async function executeWatchAction(
|
||||||
|
action,
|
||||||
|
ulcoor,
|
||||||
|
brcoor,
|
||||||
|
time,
|
||||||
|
iid,
|
||||||
|
canvasid,
|
||||||
|
) {
|
||||||
|
if (!canvasid) {
|
||||||
|
return { info: 'canvasid not defined' };
|
||||||
|
}
|
||||||
|
const ts = parseInt(time, 10);
|
||||||
|
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';
|
||||||
|
} else if (Number.isNaN(ts)) {
|
||||||
|
error = 'Invalid time given';
|
||||||
|
}
|
||||||
|
if (error) {
|
||||||
|
return { info: error };
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseCoords = validateCoorRange(ulcoor, brcoor, canvas.size);
|
||||||
|
if (typeof parseCoords === 'string') {
|
||||||
|
return { info: parseCoords };
|
||||||
|
}
|
||||||
|
const [x, y, u, v] = parseCoords;
|
||||||
|
|
||||||
|
if (u - x > 1000 || v - y > 1000) {
|
||||||
|
return { info: 'Cann not watch larger than 1000x1000 area' };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action === 'summary') {
|
||||||
|
const ret = await getSummaryFromArea(
|
||||||
|
canvasid,
|
||||||
|
x, y, u, v,
|
||||||
|
time,
|
||||||
|
iid,
|
||||||
|
);
|
||||||
|
if (typeof ret === 'string') {
|
||||||
|
return { info: ret };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
info: null,
|
||||||
|
columns: ['#pxls', 'IID', 'User', 'last', 'clr', 'ts'],
|
||||||
|
rows: (ret.length > 300) ? ret.slice(-300) : ret,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action === 'all') {
|
||||||
|
const ret = await getPixelsFromArea(
|
||||||
|
canvasid,
|
||||||
|
x, y, u, v,
|
||||||
|
time,
|
||||||
|
iid,
|
||||||
|
);
|
||||||
|
if (typeof ret === 'string') {
|
||||||
|
return { info: ret };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
info: null,
|
||||||
|
columns: ['IID', 'User', 'last', 'clr', 'ts'],
|
||||||
|
rows: ret,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return { info: 'Invalid action given' };
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Execute actions for cleaning/filtering canvas
|
* Execute actions for cleaning/filtering canvas
|
||||||
* @param action what to do
|
* @param action what to do
|
||||||
|
|
188
src/core/parsePixelLog.js
Normal file
188
src/core/parsePixelLog.js
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
import fs from 'fs';
|
||||||
|
import readline from 'readline';
|
||||||
|
|
||||||
|
import { PIXELLOGGER_PREFIX } from './logger';
|
||||||
|
import { getNamesToIds } from '../data/sql/RegUser';
|
||||||
|
import { getIdsToIps, getIPofIID } from '../data/sql/IPInfo';
|
||||||
|
import { getIPv6Subnet } from '../utils/ip';
|
||||||
|
|
||||||
|
|
||||||
|
function parseFile(cb) {
|
||||||
|
const date = new Date();
|
||||||
|
const year = date.getUTCFullYear();
|
||||||
|
let month = date.getUTCMonth() + 1;
|
||||||
|
let day = date.getUTCDate();
|
||||||
|
if (day < 10) day = `0${day}`;
|
||||||
|
if (month < 10) month = `0${month}`;
|
||||||
|
const filename = `${PIXELLOGGER_PREFIX}${year}-${month}-${day}.log`;
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const fileStream = fs.createReadStream(filename);
|
||||||
|
|
||||||
|
const rl = readline.createInterface({
|
||||||
|
input: fileStream,
|
||||||
|
});
|
||||||
|
|
||||||
|
rl.on('line', (line) => cb(line.split(' ')));
|
||||||
|
|
||||||
|
rl.on('error', (err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
rl.on('close', () => {
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get summary of users placing in area of current day
|
||||||
|
* @param canvasId id of canvas
|
||||||
|
* @param xUL, yUL, xBR, yBR area of canvs
|
||||||
|
* @param time timestamp of when to start
|
||||||
|
* @param iid Limit on one user (optional)
|
||||||
|
* @return array of parsed pixel log lines
|
||||||
|
* string if error
|
||||||
|
*/
|
||||||
|
export async function getSummaryFromArea(
|
||||||
|
canvasId,
|
||||||
|
xUL,
|
||||||
|
yUL,
|
||||||
|
xBR,
|
||||||
|
yBR,
|
||||||
|
time,
|
||||||
|
iid,
|
||||||
|
) {
|
||||||
|
const ips = {};
|
||||||
|
const uids = [];
|
||||||
|
let filterIP = null;
|
||||||
|
if (iid) {
|
||||||
|
filterIP = await getIPofIID(iid);
|
||||||
|
if (!filterIP) {
|
||||||
|
return 'Could not resolve IID to IP';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await parseFile((parts) => {
|
||||||
|
const [ts, ipFull, uid, cid, x, y,, clr] = parts;
|
||||||
|
// eslint-disable-next-line eqeqeq
|
||||||
|
if (canvasId == cid
|
||||||
|
&& ts >= time
|
||||||
|
&& x >= xUL
|
||||||
|
&& x <= xBR
|
||||||
|
&& y >= yUL
|
||||||
|
&& y <= yBR
|
||||||
|
) {
|
||||||
|
const ip = getIPv6Subnet(ipFull);
|
||||||
|
if (filterIP && ip !== filterIP) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let curVals = ips[ip];
|
||||||
|
if (!curVals) {
|
||||||
|
curVals = [0, ip, uid, 0, 0, 0, 0];
|
||||||
|
ips[ip] = curVals;
|
||||||
|
uids.push(uid);
|
||||||
|
}
|
||||||
|
curVals[0] += 1;
|
||||||
|
curVals[3] = x;
|
||||||
|
curVals[4] = y;
|
||||||
|
curVals[5] = clr;
|
||||||
|
curVals[6] = ts;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
return `Could not parse logfile: ${err.message}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uid2Name = await getNamesToIds(uids);
|
||||||
|
|
||||||
|
const ipKeys = Object.keys(ips);
|
||||||
|
const ip2Id = await getIdsToIps(ipKeys);
|
||||||
|
|
||||||
|
const rows = [];
|
||||||
|
for (let i = 0; i < ipKeys.length; i += 1) {
|
||||||
|
const [pxls, ip, uid, x, y, clr, ts] = ips[ipKeys[i]];
|
||||||
|
const userMd = (uid && uid2Name[uid])
|
||||||
|
? `@[${uid2Name[uid]}](${uid})` : 'N/A';
|
||||||
|
rows.push([
|
||||||
|
pxls,
|
||||||
|
ip2Id[ip] || 'N/A',
|
||||||
|
userMd,
|
||||||
|
`#d,${x},${y}`,
|
||||||
|
clr,
|
||||||
|
ts,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function getPixelsFromArea(
|
||||||
|
canvasId,
|
||||||
|
xUL,
|
||||||
|
yUL,
|
||||||
|
xBR,
|
||||||
|
yBR,
|
||||||
|
time,
|
||||||
|
iid,
|
||||||
|
maxRows = 300,
|
||||||
|
) {
|
||||||
|
const pixels = [];
|
||||||
|
const uids = [];
|
||||||
|
const ips = [];
|
||||||
|
let filterIP = null;
|
||||||
|
if (iid) {
|
||||||
|
filterIP = await getIPofIID(iid);
|
||||||
|
if (!filterIP) {
|
||||||
|
return 'Could not resolve IID to IP';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await parseFile((parts) => {
|
||||||
|
const [ts, ipFull, uid, cid, x, y,, clr] = parts;
|
||||||
|
// eslint-disable-next-line eqeqeq
|
||||||
|
if (canvasId == cid
|
||||||
|
&& ts >= time
|
||||||
|
&& x >= xUL
|
||||||
|
&& x <= xBR
|
||||||
|
&& y >= yUL
|
||||||
|
&& y <= yBR
|
||||||
|
) {
|
||||||
|
const ip = getIPv6Subnet(ipFull);
|
||||||
|
if (filterIP && ip !== filterIP) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pixels.push([ip, uid, x, y, clr, ts]);
|
||||||
|
if (!ips.includes(ip)) {
|
||||||
|
ips.push(ip);
|
||||||
|
uids.push(uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
return `Could not parse logfile: ${err.message}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uid2Name = await getNamesToIds(uids);
|
||||||
|
const ip2Id = await getIdsToIps(ips);
|
||||||
|
|
||||||
|
const pixelF = (pixels.length > 300) ? pixels.slice(maxRows * -1) : pixels;
|
||||||
|
|
||||||
|
const rows = [];
|
||||||
|
for (let i = 0; i < pixelF.length; i += 1) {
|
||||||
|
const [ip, uid, x, y, clr, ts] = pixelF[i];
|
||||||
|
const userMd = (uid && uid2Name[uid])
|
||||||
|
? `@[${uid2Name[uid]}](${uid})` : 'N/A';
|
||||||
|
const id = ip2Id[ip] || 'N/A';
|
||||||
|
rows.push([
|
||||||
|
id,
|
||||||
|
userMd,
|
||||||
|
`#d,${x},${y}`,
|
||||||
|
clr,
|
||||||
|
ts,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
}
|
|
@ -87,15 +87,34 @@ const IPInfo = sequelize.define('IPInfo', {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export async function getIPofIID(uuid) {
|
||||||
|
let result = null;
|
||||||
|
try {
|
||||||
|
result = IPInfo.findOne({
|
||||||
|
attributes: ['ip'],
|
||||||
|
where: { uuid },
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (result) {
|
||||||
|
return result.id;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
export async function getIdsToIps(ips) {
|
export async function getIdsToIps(ips) {
|
||||||
const result = IPInfo.findAll({
|
const ipToIdMap = {};
|
||||||
|
if (!ips.length) {
|
||||||
|
return ipToIdMap;
|
||||||
|
}
|
||||||
|
const result = await IPInfo.findAll({
|
||||||
attributes: ['ip', 'uuid'],
|
attributes: ['ip', 'uuid'],
|
||||||
where: {
|
where: {
|
||||||
ip: ips,
|
ip: ips,
|
||||||
},
|
},
|
||||||
raw: true,
|
raw: true,
|
||||||
});
|
});
|
||||||
const ipToIdMap = {};
|
|
||||||
result.forEach((obj) => {
|
result.forEach((obj) => {
|
||||||
ipToIdMap[obj.ip] = obj.uuid;
|
ipToIdMap[obj.ip] = obj.uuid;
|
||||||
});
|
});
|
||||||
|
|
|
@ -182,14 +182,17 @@ export async function findIdByNameOrId(searchString) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getNamesToIds(ids) {
|
export async function getNamesToIds(ids) {
|
||||||
const result = RegUser.findAll({
|
const idToNameMap = {};
|
||||||
|
if (!ids.length) {
|
||||||
|
return idToNameMap;
|
||||||
|
}
|
||||||
|
const result = await RegUser.findAll({
|
||||||
attributes: ['id', 'name'],
|
attributes: ['id', 'name'],
|
||||||
where: {
|
where: {
|
||||||
id: ids,
|
id: ids,
|
||||||
},
|
},
|
||||||
raw: true,
|
raw: true,
|
||||||
});
|
});
|
||||||
const idToNameMap = {};
|
|
||||||
result.forEach((obj) => {
|
result.forEach((obj) => {
|
||||||
idToNameMap[obj.id] = obj.name;
|
idToNameMap[obj.id] = obj.name;
|
||||||
});
|
});
|
||||||
|
|
|
@ -18,6 +18,7 @@ import {
|
||||||
executeProtAction,
|
executeProtAction,
|
||||||
executeRollback,
|
executeRollback,
|
||||||
executeCleanerAction,
|
executeCleanerAction,
|
||||||
|
executeWatchAction,
|
||||||
getModList,
|
getModList,
|
||||||
removeMod,
|
removeMod,
|
||||||
makeMod,
|
makeMod,
|
||||||
|
@ -99,6 +100,21 @@ router.post('/', upload.single('image'), async (req, res, next) => {
|
||||||
res.status(200).send(ret);
|
res.status(200).send(ret);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (req.body.watchaction) {
|
||||||
|
const {
|
||||||
|
watchaction, ulcoor, brcoor, time, iid, canvasid,
|
||||||
|
} = req.body;
|
||||||
|
const ret = await executeWatchAction(
|
||||||
|
watchaction,
|
||||||
|
ulcoor,
|
||||||
|
brcoor,
|
||||||
|
time,
|
||||||
|
iid,
|
||||||
|
canvasid,
|
||||||
|
);
|
||||||
|
res.status(200).json(ret);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (req.body.cleaneraction) {
|
if (req.body.cleaneraction) {
|
||||||
const {
|
const {
|
||||||
cleaneraction, ulcoor, brcoor, canvasid,
|
cleaneraction, ulcoor, brcoor, canvasid,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user