diff --git a/src/components/Admintools.jsx b/src/components/Admintools.jsx
index e746fb2..05e9428 100644
--- a/src/components/Admintools.jsx
+++ b/src/components/Admintools.jsx
@@ -88,6 +88,7 @@ function Admintools() {
- {['ban', 'unban', 'whitelist', 'unwhitelist'].map((opt) => (
-
- ))}
+ {['ban', 'unban', 'whitelist', 'unwhitelist', 'iidtoip']
+ .map((opt) => (
+
+ ))}
diff --git a/src/components/ModWatchtools.jsx b/src/components/ModWatchtools.jsx
index 1b01b55..a61170b 100644
--- a/src/components/ModWatchtools.jsx
+++ b/src/components/ModWatchtools.jsx
@@ -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 (
{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`}
+
+ {(table) && (
+
+
+
+
+
+ {columns.slice(1).map((col) => (
+ {col} |
+ ))}
+
+
+
+ {rows.map((row) => (
+
+ {row.slice(1).map((val, ind) => {
+ const type = types[ind + 1];
+ switch (type) {
+ case 'ts': {
+ const date = new Date(val);
+ return (
+
+ {`${date.getHours()}:${date.getMinutes()}`}
+ |
+ );
+ }
+ case 'clr': {
+ const color = colors[val];
+ const style = (color) ? { backgroundColor: color } : {};
+ return ({val} | );
+ }
+ case 'coord': {
+ const { ident } = canvases[selectedCanvas];
+ const coords = `./#${ident},${val},47`;
+ return (
+
+ {val}
+ |
+ );
+ }
+ case 'flag': {
+ const flag = val.toLowerCase();
+ return (
+ ![{val}]({`${window.ssv.assetserver}/cf/${flag}.gif`}) |
+ );
+ }
+ case 'user': {
+ const seperator = val.lastIndexOf(',');
+ if (seperator === -1) {
+ return ({val} | );
+ }
+ return (
+
+ {val.slice(0, seperator)}
+ |
+ );
+ }
+ default: {
+ return ({val} | );
+ }
+ }
+ })}
+
+ ))}
+
+
+
+ )}
);
}
+// possible types:
+// 'coord', 'clr', 'ts', 'user', 'uuid', 'string', 'number', 'flag'
+
export default React.memo(ModWatchtools);
diff --git a/src/core/adminfunctions.js b/src/core/adminfunctions.js
index 2907f33..8e55ccf 100644
--- a/src/core/adminfunctions.js
+++ b/src/core/adminfunctions.js
@@ -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' };
diff --git a/src/core/parsePixelLog.js b/src/core/parsePixelLog.js
index fba94fb..e0d118c 100644
--- a/src/core/parsePixelLog.js
+++ b/src/core/parsePixelLog.js
@@ -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,
+ };
}
diff --git a/src/data/sql/IPInfo.js b/src/data/sql/IPInfo.js
index 1687e75..f1d5cf2 100644
--- a/src/data/sql/IPInfo.js
+++ b/src/data/sql/IPInfo.js
@@ -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;
}
diff --git a/src/data/sql/RegUser.js b/src/data/sql/RegUser.js
index 7f49c7e..b5d869e 100644
--- a/src/data/sql/RegUser.js
+++ b/src/data/sql/RegUser.js
@@ -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;
}