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();
|
||||
data.append('watchaction', action);
|
||||
data.append('canvasid', canvas);
|
||||
data.append('ulcoor', tlcoords);
|
||||
data.append('brcoor', brcoords);
|
||||
data.append('time', time);
|
||||
|
|
|
@ -19,6 +19,10 @@ import {
|
|||
imageABGR2Canvas,
|
||||
protectCanvasArea,
|
||||
} from './Image';
|
||||
import {
|
||||
getSummaryFromArea,
|
||||
getPixelsFromArea,
|
||||
} from './parsePixelLog';
|
||||
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
|
||||
* @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) {
|
||||
const result = IPInfo.findAll({
|
||||
const ipToIdMap = {};
|
||||
if (!ips.length) {
|
||||
return ipToIdMap;
|
||||
}
|
||||
const result = await IPInfo.findAll({
|
||||
attributes: ['ip', 'uuid'],
|
||||
where: {
|
||||
ip: ips,
|
||||
},
|
||||
raw: true,
|
||||
});
|
||||
const ipToIdMap = {};
|
||||
result.forEach((obj) => {
|
||||
ipToIdMap[obj.ip] = obj.uuid;
|
||||
});
|
||||
|
|
|
@ -182,14 +182,17 @@ export async function findIdByNameOrId(searchString) {
|
|||
}
|
||||
|
||||
export async function getNamesToIds(ids) {
|
||||
const result = RegUser.findAll({
|
||||
const idToNameMap = {};
|
||||
if (!ids.length) {
|
||||
return idToNameMap;
|
||||
}
|
||||
const result = await RegUser.findAll({
|
||||
attributes: ['id', 'name'],
|
||||
where: {
|
||||
id: ids,
|
||||
},
|
||||
raw: true,
|
||||
});
|
||||
const idToNameMap = {};
|
||||
result.forEach((obj) => {
|
||||
idToNameMap[obj.id] = obj.name;
|
||||
});
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
executeProtAction,
|
||||
executeRollback,
|
||||
executeCleanerAction,
|
||||
executeWatchAction,
|
||||
getModList,
|
||||
removeMod,
|
||||
makeMod,
|
||||
|
@ -99,6 +100,21 @@ router.post('/', upload.single('image'), async (req, res, next) => {
|
|||
res.status(200).send(ret);
|
||||
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) {
|
||||
const {
|
||||
cleaneraction, ulcoor, brcoor, canvasid,
|
||||
|
|
Loading…
Reference in New Issue
Block a user