parse pixel table

add iidtoip admin function
This commit is contained in:
HF 2022-08-02 19:58:23 +02:00
parent 86adb162dc
commit ae878d4518
6 changed files with 303 additions and 84 deletions

View File

@ -88,6 +88,7 @@ function Admintools() {
<div style={{
borderStyle: 'solid',
borderColor: '#D4D4D4',
userSelect: 'text',
borderWidth: 2,
padding: 5,
display: 'inline-block',
@ -121,13 +122,14 @@ function Admintools() {
selectIPAction(sel.options[sel.selectedIndex].value);
}}
>
{['ban', 'unban', 'whitelist', 'unwhitelist'].map((opt) => (
<option
value={opt}
>
{opt}
</option>
))}
{['ban', 'unban', 'whitelist', 'unwhitelist', 'iidtoip']
.map((opt) => (
<option
value={opt}
>
{opt}
</option>
))}
</select>
<br />
<textarea rows="10" cols="17" id="iparea" /><br />

View File

@ -71,9 +71,11 @@ async function submitWatchAction(
function ModWatchtools() {
const [selectedCanvas, selectCanvas] = useState(0);
const [colors, setColors] = useState([]);
const [tlcoords, selectTLCoords] = useState(keepState.tlcoords);
const [brcoords, selectBRCoords] = useState(keepState.brcoords);
const [interval, selectInterval] = useState(keepState.interval);
const [table, setTable] = useState(null);
const [iid, selectIid] = useState(keepState.iid);
const [resp, setResp] = useState(null);
const [submitting, setSubmitting] = useState(false);
@ -90,6 +92,25 @@ function ModWatchtools() {
selectCanvas(canvasId);
}, [canvasId]);
useEffect(() => {
const colorsRGB = canvases[selectedCanvas].colors;
const newColors = [];
for (let i = 0; i < colorsRGB.length; i += 1) {
const [r, g, b] = colorsRGB[i];
newColors.push(`rgb(${r},${g},${b})`);
}
setColors(newColors);
}, [selectedCanvas]);
let columns;
let types;
let rows;
if (table) {
columns = table.columns;
types = table.types;
rows = table.rows;
}
return (
<div style={{ textAlign: 'center', paddingLeft: '5%', paddingRight: '5%' }}>
{resp && (
@ -225,6 +246,13 @@ function ModWatchtools() {
(ret) => {
setSubmitting(false);
setResp(ret.info);
if (ret.rows) {
setTable({
columns: ret.columns,
types: ret.types,
rows: ret.rows,
});
}
},
);
}}
@ -248,14 +276,104 @@ function ModWatchtools() {
(ret) => {
setSubmitting(false);
setResp(ret.info);
if (ret.rows) {
setTable({
columns: ret.columns,
types: ret.types,
rows: ret.rows,
});
}
},
);
}}
>
{(submitting) ? '...' : t`Get Users`}
</button>
<br />
{(table) && (
<React.Fragment key="pxltable">
<div className="modaldivider" />
<table
style={{
userSelect: 'text',
fontSize: 11,
}}
>
<thead>
<tr>
{columns.slice(1).map((col) => (
<th>{col}</th>
))}
</tr>
</thead>
<tbody>
{rows.map((row) => (
<tr key={row[0]}>
{row.slice(1).map((val, ind) => {
const type = types[ind + 1];
switch (type) {
case 'ts': {
const date = new Date(val);
return (
<td title={date.toLocaleDateString()}>
{`${date.getHours()}:${date.getMinutes()}`}
</td>
);
}
case 'clr': {
const color = colors[val];
const style = (color) ? { backgroundColor: color } : {};
return (<td style={style}>{val}</td>);
}
case 'coord': {
const { ident } = canvases[selectedCanvas];
const coords = `./#${ident},${val},47`;
return (
<td>
<a href={coords}>{val}</a>
</td>
);
}
case 'flag': {
const flag = val.toLowerCase();
return (
<td title={val}><img
style={{
height: '1em',
imageRendering: 'crisp-edges',
}}
alt={val}
src={`${window.ssv.assetserver}/cf/${flag}.gif`}
/></td>
);
}
case 'user': {
const seperator = val.lastIndexOf(',');
if (seperator === -1) {
return (<td>{val}</td>);
}
return (
<td title={val.slice(seperator + 1)}>
{val.slice(0, seperator)}
</td>
);
}
default: {
return (<td>{val}</td>);
}
}
})}
</tr>
))}
</tbody>
</table>
</React.Fragment>
)}
</div>
);
}
// possible types:
// 'coord', 'clr', 'ts', 'user', 'uuid', 'string', 'number', 'flag'
export default React.memo(ModWatchtools);

View File

@ -13,6 +13,7 @@ import { getIPv6Subnet } from '../utils/ip';
import { validateCoorRange } from '../utils/validation';
import CanvasCleaner from './CanvasCleaner';
import { Blacklist, Whitelist, RegUser } from '../data/sql';
import { getIPofIID } from '../data/sql/IPInfo';
// eslint-disable-next-line import/no-unresolved
import canvases from './canvases.json';
import {
@ -34,15 +35,20 @@ import rollbackCanvasArea from './rollback';
export async function executeIPAction(action, ips, logger = null) {
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];
const ip = ipArray[i].trim();
if (action === 'iidtoip') {
const resIp = await getIPofIID(ip);
const idPart = ip.slice(0, ip.indexOf('-'));
if (resIp) {
out += `${idPart}: ${resIp}\n`;
} else {
out += `${idPart}: N/A\n`;
}
continue;
}
if (!ip || ip.length < 8 || ip.indexOf(' ') !== -1) {
out += `Couln't parse ${action} ${ip}\n`;
continue;
@ -210,8 +216,11 @@ export async function executeWatchAction(
}
const [x, y, u, v] = parseCoords;
if (u - x > 1000 || v - y > 1000) {
return { info: 'Cann not watch larger than 1000x1000 area' };
if ((u - x > 1000 || v - y > 1000)
&& Date.now() - ts > 5 * 60 * 1000
&& !iid
) {
return { info: 'Cann not watch so many pixels' };
}
if (action === 'summary') {
@ -224,11 +233,7 @@ export async function executeWatchAction(
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,
};
return ret;
}
if (action === 'all') {
@ -241,11 +246,7 @@ export async function executeWatchAction(
if (typeof ret === 'string') {
return { info: ret };
}
return {
info: null,
columns: ['IID', 'User', 'last', 'clr', 'ts'],
rows: ret,
};
return ret;
}
return { info: 'Invalid action given' };

View File

@ -3,7 +3,11 @@ import readline from 'readline';
import { PIXELLOGGER_PREFIX } from './logger';
import { getNamesToIds } from '../data/sql/RegUser';
import { getIdsToIps, getIPofIID } from '../data/sql/IPInfo';
import {
getIdsToIps,
getInfoToIps,
getIPofIID,
} from '../data/sql/IPInfo';
import { getIPv6Subnet } from '../utils/ip';
@ -64,10 +68,11 @@ export async function getSummaryFromArea(
}
try {
await parseFile((parts) => {
const [ts, ipFull, uid, cid, x, y,, clr] = parts;
// eslint-disable-next-line eqeqeq
if (canvasId == cid
&& ts >= time
const [tsStr, ipFull, uidStr, cid, x, y,, clrStr] = parts;
const ts = parseInt(tsStr, 10);
if (ts >= time
// eslint-disable-next-line eqeqeq
&& canvasId == cid
&& x >= xUL
&& x <= xBR
&& y >= yUL
@ -77,6 +82,8 @@ export async function getSummaryFromArea(
if (filterIP && ip !== filterIP) {
return;
}
const clr = parseInt(clrStr, 10);
const uid = parseInt(uidStr, 10);
let curVals = ips[ip];
if (!curVals) {
curVals = [0, ip, uid, 0, 0, 0, 0];
@ -97,24 +104,63 @@ export async function getSummaryFromArea(
const uid2Name = await getNamesToIds(uids);
const ipKeys = Object.keys(ips);
const ip2Id = await getIdsToIps(ipKeys);
const ip2Info = await getInfoToIps(ipKeys);
let printIIDs = false;
let printUsers = false;
const columns = ['rid', '#'];
const types = ['number', 'number'];
if (ip2Info.size > 0) {
printIIDs = true;
columns.push('IID', 'ct', 'cidr', 'org', 'pc');
types.push('uuid', 'flag', 'string', 'string', 'string');
}
if (uid2Name.size > 0) {
printUsers = true;
columns.push('User');
types.push('user');
}
columns.push('last', 'clr', 'time');
types.push('coord', 'clr', 'ts');
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,
]);
const row = [i, pxls];
if (printIIDs) {
const ipInfo = ip2Info.get(ip);
if (!ipInfo) {
row.push('N/A', 'xx', 'N/A', 'N/A', 'N/A');
}
let { pcheck } = ipInfo;
if (pcheck) {
const seperator = pcheck.indexOf(',');
if (seperator !== -1) {
pcheck = pcheck.slice(0, seperator);
}
}
row.push(
ipInfo.uuid || 'N/A',
ipInfo.country || 'xx',
ipInfo.cidr || 'N/A',
ipInfo.org || 'N/A',
pcheck || 'N/A',
);
}
if (printUsers) {
const userMd = (uid && uid2Name.has(uid))
? `${uid2Name.get(uid)},${uid}` : 'N/A';
row.push(userMd);
}
row.push(`${x},${y}`, clr, ts);
rows.push(row);
}
return rows;
return {
columns,
types,
rows,
};
}
@ -140,10 +186,11 @@ export async function getPixelsFromArea(
}
try {
await parseFile((parts) => {
const [ts, ipFull, uid, cid, x, y,, clr] = parts;
// eslint-disable-next-line eqeqeq
if (canvasId == cid
&& ts >= time
const [tsStr, ipFull, uidStr, cid, x, y,, clrStr] = parts;
const ts = parseInt(tsStr, 10);
if (ts >= time
// eslint-disable-next-line eqeqeq
&& canvasId == cid
&& x >= xUL
&& x <= xBR
&& y >= yUL
@ -153,6 +200,8 @@ export async function getPixelsFromArea(
if (filterIP && ip !== filterIP) {
return;
}
const clr = parseInt(clrStr, 10);
const uid = parseInt(uidStr, 10);
pixels.push([ip, uid, x, y, clr, ts]);
if (!ips.includes(ip)) {
ips.push(ip);
@ -169,20 +218,42 @@ export async function getPixelsFromArea(
const pixelF = (pixels.length > 300) ? pixels.slice(maxRows * -1) : pixels;
let printIIDs = false;
let printUsers = false;
const columns = ['#'];
const types = ['number'];
if (!filterIP && ip2Id.size > 0) {
printIIDs = true;
columns.push('IID');
types.push('uuid');
}
if (!filterIP && uid2Name.size > 0) {
printUsers = true;
columns.push('User');
types.push('user');
}
columns.push('coord', 'clr', 'time');
types.push('coord', 'clr', 'ts');
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,
]);
const row = [i];
if (printIIDs) {
row.push(ip2Id.get(ip) || 'N/A');
}
if (printUsers) {
const userMd = (uid && uid2Name.has(uid))
? `${uid2Name.get(uid)},${uid}` : 'N/A';
row.push(userMd);
}
row.push(`${x},${y}`, clr, ts);
rows.push(row);
}
return rows;
return {
columns,
types,
rows,
};
}

View File

@ -90,34 +90,57 @@ const IPInfo = sequelize.define('IPInfo', {
export async function getIPofIID(uuid) {
let result = null;
try {
result = IPInfo.findOne({
result = await IPInfo.findOne({
attributes: ['ip'],
where: { uuid },
raw: true,
});
} catch {
return null;
}
if (result) {
return result.id;
return result.ip;
}
return null;
}
export async function getIdsToIps(ips) {
const ipToIdMap = {};
if (!ips.length) {
const ipToIdMap = new Map();
if (!ips.length || ips.length > 100) {
return ipToIdMap;
}
const result = await IPInfo.findAll({
attributes: ['ip', 'uuid'],
where: {
ip: ips,
},
raw: true,
});
result.forEach((obj) => {
ipToIdMap[obj.ip] = obj.uuid;
});
try {
const result = await IPInfo.findAll({
attributes: ['ip', 'uuid'],
where: { ip: ips },
raw: true,
});
result.forEach((obj) => {
ipToIdMap.set(obj.ip, obj.uuid);
});
} catch {
// nothing
}
return ipToIdMap;
}
export async function getInfoToIps(ips) {
const ipToIdMap = new Map();
if (!ips.length || ips.length > 100) {
return ipToIdMap;
}
try {
const result = await IPInfo.findAll({
attributes: ['ip', 'uuid', 'country', 'cidr', 'org', 'pcheck'],
where: { ip: ips },
raw: true,
});
result.forEach((obj) => {
ipToIdMap.set(obj.ip, obj);
});
} catch {
// nothing
}
return ipToIdMap;
}

View File

@ -182,20 +182,24 @@ export async function findIdByNameOrId(searchString) {
}
export async function getNamesToIds(ids) {
const idToNameMap = {};
if (!ids.length) {
const idToNameMap = new Map();
if (!ids.length || ids.length > 100) {
return idToNameMap;
}
const result = await RegUser.findAll({
attributes: ['id', 'name'],
where: {
id: ids,
},
raw: true,
});
result.forEach((obj) => {
idToNameMap[obj.id] = obj.name;
});
try {
const result = await RegUser.findAll({
attributes: ['id', 'name'],
where: {
id: ids,
},
raw: true,
});
result.forEach((obj) => {
idToNameMap.set(obj.id, obj.name);
});
} catch {
// nothing
}
return idToNameMap;
}