fix minor issues

This commit is contained in:
HF 2022-08-06 05:59:33 +02:00
parent be8f94b368
commit 0e78dea560
25 changed files with 584 additions and 310 deletions

View File

@ -7,12 +7,12 @@ import { t } from 'ttag';
async function submitIPAction(
action,
vallist,
callback,
) {
const data = new FormData();
const iplist = document.getElementById('iparea').value;
data.append('ip', iplist);
data.append('ipaction', action);
data.append('ip', vallist);
const resp = await fetch('./api/modtools', {
credentials: 'include',
method: 'POST',
@ -72,8 +72,9 @@ async function submitMakeMod(
function Admintools() {
const [iPAction, selectIPAction] = useState('ban');
const [iPAction, selectIPAction] = useState('iidtoip');
const [modName, selectModName] = useState('');
const [txtval, setTxtval] = useState('');
const [resp, setResp] = useState(null);
const [modlist, setModList] = useState([]);
const [submitting, setSubmitting] = useState(false);
@ -114,9 +115,10 @@ function Admintools() {
selectIPAction(sel.options[sel.selectedIndex].value);
}}
>
{['iidtoip']
{['iidtoip', 'iptoiid']
.map((opt) => (
<option
key={opt}
value={opt}
>
{opt}
@ -124,7 +126,12 @@ function Admintools() {
))}
</select>
<br />
<textarea rows="10" cols="17" id="iparea" /><br />
<textarea
rows="10"
cols="17"
value={txtval}
onChange={(e) => setTxtval(e.target.value)}
/><br />
<button
type="button"
onClick={() => {
@ -134,9 +141,10 @@ function Admintools() {
setSubmitting(true);
submitIPAction(
iPAction,
txtval,
(ret) => {
setSubmitting(false);
setResp(ret);
setTxtval(ret);
},
);
}}
@ -157,6 +165,7 @@ function Admintools() {
<div
role="button"
tabIndex={0}
key={mod[0]}
onClick={() => {
if (submitting) {
return;

View File

@ -62,19 +62,19 @@ const Alert = () => {
className={(open && render) ? 'Alert show' : 'Alert'}
>
<h2>{title}</h2>
<p className="modaltext">
{message}
</p>
<div>
{(Content) ? (
<Content close={close} />
) : (
<button
type="button"
onClick={close}
>{btn}</button>
)}
</div>
{(message) && (
<p className="modaltext">
{message}
</p>
)}
{(Content) ? (
<Content close={close} />
) : (
<button
type="button"
onClick={close}
>{btn}</button>
)}
</div>
</div>
)

View File

@ -3,9 +3,11 @@
*/
import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
import { t } from 'ttag';
import useInterval from './hooks/interval';
import { showHelpModal } from '../store/actions';
import {
largeDurationToString,
} from '../core/utils';
@ -14,17 +16,20 @@ import { requestBanInfo } from '../store/actions/fetch';
const BanInfo = ({ close }) => {
const [errors, setErrors] = useState([]);
const [reason, setReason] = useState('');
const [reason, setReason] = useState(null);
const [mod, setMod] = useState(null);
const [expireTs, setExpireTs] = useState(0);
const [expire, setExpire] = useState(null);
const [submitting, setSubmitting] = useState(false);
const handleSubmit = async (evt) => {
evt.preventDefault();
const dispatch = useDispatch();
const handleSubmit = async () => {
if (submitting) {
return;
}
setSubmitting(true);
setErrors([]);
const info = await requestBanInfo();
setSubmitting(false);
if (info.errors) {
@ -32,37 +37,64 @@ const BanInfo = ({ close }) => {
return;
}
const {
ts,
sleft,
mod: newMod,
reason: newReason,
} = info;
setExpireTs(ts);
const tsDate = new Date(ts);
setExpire(tsDate.toLocaleString);
if (sleft) {
const tsDate = new Date(Date.now() + sleft * 1000);
setExpireTs(sleft);
setExpire(tsDate.toLocaleString());
}
setMod(newMod);
setReason(newReason);
};
useInterval(() => {
console.log('do');
if (expireTs) {
if (expireTs > 0) {
setExpireTs(expireTs - 1);
if (expireTs === 1) {
handleSubmit();
}
}
}, 1000);
/* eslint-disable max-len */
return (
<div>
<div style={{ userSelect: 'text' }}>
<p className="modaltext">
{t`You are banned. You think it is unjustifed? Check out the `}
<span
role="button"
tabIndex={0}
className="modallink"
onClick={() => {
dispatch(showHelpModal());
close();
}}
>{t`Help`}</span>
{t` on how to appeal.`}
</p>
{errors.map((error) => (
<p key={error} className="errormessage">
<span>{t`Error`}</span>:&nbsp;{error}
</p>
))}
{(reason) && (
<>
<React.Fragment key="rea">
<h3 className="modaltitle">{t`Reason`}:</h3>
<p className="modaltext">{reason}</p>
</>
</React.Fragment>
)}
{(expireTs) && (
<>
{(mod) && (
<React.Fragment key="mod">
<h3 className="modaltitle">{t`By Mod`}:</h3>
<p className="modaltext">{mod}</p>
</React.Fragment>
)}
{(expireTs > 0) && (
<React.Fragment key="exp">
<h3 className="modaltitle">{t`Duration`}:</h3>
<p className="modaltext">
{t`Your ban expires at `}
@ -74,17 +106,27 @@ const BanInfo = ({ close }) => {
{largeDurationToString(expireTs)}
</span>
</p>
</>
</React.Fragment>
)}
{(expireTs < 0) && (
<React.Fragment key="nb">
<h3 className="modaltitle">{t`Unbanned`}:</h3>
<p className="modaltext">{t`Now that you have seen this message, you are no longer banned.`}</p>
</React.Fragment>
)}
<p>
<button
type="button"
style={{ fontSize: 16 }}
onClick={handleSubmit}
>
{(submitting) ? '...' : t`Why?`}
</button>
&nbsp;
{(!reason) && (
<React.Fragment key="btnr">
<button
type="button"
style={{ fontSize: 16 }}
onClick={handleSubmit}
>
{(submitting) ? '...' : t`Why?`}
</button>
&nbsp;
</React.Fragment>
)}
<button
type="submit"
style={{ fontSize: 16 }}

59
src/components/GetIID.jsx Normal file
View File

@ -0,0 +1,59 @@
/*
* show IID
*/
import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
import { t } from 'ttag';
import { notify } from '../store/actions';
import copyTextToClipboard from '../utils/clipboard';
import {
requestIID,
} from '../store/actions/fetch';
const GetIID = () => {
const [iid, setIID] = useState('');
const [submitting, setSubmitting] = useState(false);
const dispatch = useDispatch();
return (
<p>
<input
style={{
display: 'inline-block',
width: '100%',
maxWidth: '18em',
}}
readOnly
value={iid}
/>
{(!iid)
? (
<button
key="subtn"
type="button"
onClick={async () => {
setSubmitting(true);
const resp = await requestIID();
if (resp.iid) {
setIID(resp.iid);
}
setSubmitting(false);
}}
>{(submitting) ? '...' : t`Get IID`}</button>
) : (
<button
key="cobtn"
type="button"
onClick={() => {
copyTextToClipboard(iid);
dispatch(notify(t`Copied!`));
}}
>{t`Copy`}</button>
)}
</p>
);
};
export default React.memo(GetIID);

View File

@ -43,6 +43,7 @@ function LanguageSelect() {
{
langs.map(([l]) => (
<option
key={l}
value={l}
>
{l.toUpperCase()}

View File

@ -14,11 +14,18 @@ async function submitIIDAction(
duration,
callback,
) {
let time = parseInterval(duration);
if (time === 0 && duration !== '0') {
callback(t`You must enter an IID`);
return;
}
if (!iid) {
callback(t`You must enter an IID`);
return;
}
const time = Date.now() + parseInterval(duration);
if (time > 0) {
time += Date.now();
}
const data = new FormData();
data.append('iidaction', action);
data.append('reason', reason);
@ -61,7 +68,7 @@ function ModIIDtools() {
))}
</select>
{(iIDAction === 'ban') && (
<>
<React.Fragment key="ban">
<p>{t`Reason`}</p>
<input
maxLength="200"
@ -88,7 +95,7 @@ function ModIIDtools() {
/>
{t`(0 = infinite)`}
</p>
</>
</React.Fragment>
)}
<p className="modalcotext">
{' IID: '}

View File

@ -7,6 +7,7 @@ import React, { useState, useEffect } from 'react';
import { useSelector, shallowEqual } from 'react-redux';
import { t } from 'ttag';
import copyTextToClipboard from '../utils/clipboard';
import { parseInterval } from '../core/utils';
const keepState = {
@ -341,11 +342,22 @@ function ModWatchtools() {
);
}
case 'cid': {
const cid = (cidColumn > 0)
? row[cidColumn] : selectedCanvas;
const ident = canvases[cid] && canvases[cid].ident;
const ident = canvases[val] && canvases[val].ident;
return (<td>{ident}</td>);
}
case 'uuid': {
return (
<td>
<span
role="button"
tabIndex={-1}
style={{ cursor: 'pointer' }}
title={t`Copy to Clipboard`}
onClick={() => copyTextToClipboard(val)}
>{val}</span>
</td>
);
}
case 'user': {
const seperator = val.lastIndexOf(',');
if (seperator === -1) {

View File

@ -7,8 +7,9 @@ import { c, t, jt } from 'ttag';
import { GiMouse } from 'react-icons/gi';
import { MdTouchApp } from 'react-icons/md';
/* eslint-disable max-len */
import GetIID from '../GetIID';
/* eslint-disable max-len */
const Help = () => {
const bindG = <kbd>{c('keybinds').t`G`}</kbd>;
@ -33,7 +34,6 @@ const Help = () => {
const starhouseLink = <a href="https://twitter.com/starhousedev">starhouse </a>;
const vinikLink = <a href="https://twitter.com/Vinikdev">Vinikdev</a>;
const guildedLink = <a href="https://pixelplanet.fun/guilded">guilded</a>;
const getIPLink = <a href="https://www.whatismyip.com/">{t`your IP`}</a>;
const mailLink = <a href="mailto:admin@pixelplanet.fun">admin@pixelplanet.fun</a>;
return (
@ -52,9 +52,10 @@ pixels and 7s on already set pixels.`}<br />
<p className="modaltitle">{t`Map Data`}</p>
<p className="modaltext">{t`The bare map data that we use, together with converted OpenStreetMap tiles for orientation, \
can be downloaded from mega.nz here: `}<a href="https://mega.nz/#!JpkBwAbJ!EnSLlZmKv3kEBE0HDhakTgAZZycD3ELjduajJxPGaXo">pixelplanetmap.zip</a> (422MB)</p>
<p className="modaltitle">{t`Detected as Proxy?`}</p>
<p className="modaltitle">{t`Banned? Detected as Proxy?`}</p>
<div className="modaltext">
<p>{jt`If you got detected as proxy, but you are none, please go to our ${guildedLink} or send us an e-mail with ${getIPLink} to ${mailLink}. Do not post your IP anywhere else. We are sorry for the inconvenience.`}</p>
<p>{jt`If you got detected as proxy, but you are none, or think that you got wrongfully banned, please go to our ${guildedLink} or send us an e-mail to ${mailLink} and include the following IID:`}</p>
<GetIID />
</div>
<h3 className="modaltitle">2D {t`Controls`}</h3>
<div className="modaltext" style={{ lineHeight: 1.8 }}>

View File

@ -26,7 +26,11 @@ import {
banIP,
unbanIP,
} from '../data/sql/Ban';
import { getInfoToIp, getIPofIID } from '../data/sql/IPInfo';
import {
getInfoToIp,
getIPofIID,
getIIDofIP,
} from '../data/sql/IPInfo';
// eslint-disable-next-line import/no-unresolved
import canvases from './canvases.json';
import {
@ -48,31 +52,25 @@ import rollbackCanvasArea from './rollback';
* @return text of success
*/
export async function executeIPAction(action, ips, logger = null) {
const ipArray = ips.split('\n');
const valueArray = ips.split('\n');
let out = '';
for (let i = 0; i < ipArray.length; i += 1) {
const ip = ipArray[i].trim();
for (let i = 0; i < valueArray.length; i += 1) {
const value = valueArray[i].trim();
if (!value) {
continue;
}
if (logger) logger(`${action} ${value}`);
if (action === 'iidtoip') {
const resIp = await getIPofIID(ip);
const iidPart = ip.slice(0, ip.indexOf('-'));
if (resIp) {
out += `${iidPart}: ${resIp}\n`;
} else {
out += `${iidPart}: N/A\n`;
}
const ip = await getIPofIID(value);
out += (ip) ? `${ip}\n` : `${value}\n`;
continue;
}
if (!ip || ip.length < 8 || ip.indexOf(' ') !== -1) {
out += `Couln't parse ${action} ${ip}\n`;
continue;
}
if (logger) logger(`${action} ${ip}`);
switch (action) {
default:
return `Failed to ${action} ${ip}\n`;
if (action === 'iptoiid') {
const iid = await getIIDofIP(value);
out += (iid) ? `${iid}\n` : `${value}\n`;
}
}
return out;
@ -98,10 +96,12 @@ export async function executeIIDAction(
}
const iidPart = iid.slice(0, iid.indexOf('-'));
if (logger) logger(`${action} ${iid} ${ip}`);
switch (action) {
case 'status': {
const allowed = await isIPAllowed(ip, true);
let out = `Allowed to place: ${allowed.allowed}`;
let out = `Allowed to place: ${allowed.allowed}\n`;
const info = await getInfoToIp(ip);
out += `Country: ${info.country}\n`
+ `CIDR: ${info.cidr}\n`
@ -119,7 +119,8 @@ export async function executeIIDAction(
if (!ban) {
out += 'banned: false\n';
} else {
out += `reason: ${ban.reason}\n`;
out += 'banned: true\n'
+ `reason: ${ban.reason}\n`;
if (ban.expires) {
out += `expires: ${ban.expires.toLocaleString()}\n`;
}
@ -140,28 +141,27 @@ export async function executeIIDAction(
return `${iidPart} would have gotten captcha anyway`;
}
case 'ban': {
if (expire && expire < Date.now()) {
const expireTs = parseInt(expire, 10);
if (Number.isNaN(expireTs) || (expireTs && expireTs < Date.now())) {
return 'No valid expiration time';
}
if (!reason) {
if (!reason || !reason.trim()) {
return 'No reason specified';
}
const ret = await banIP(ip, reason, expire || null, muid);
const ret = await banIP(ip, reason, expireTs || null, muid);
if (ret) {
await cleanCacheForIP(ip);
return 'Successfully banned user';
}
return 'User is already banned';
return 'Updated existing ban of user';
}
case 'unban': {
const ret = await unbanIP(ip);
if (ret) {
await cleanCacheForIP(ip);
return 'Successfully unbanned user';
}
return 'User is not banned';
}
case 'Whitelist': {
case 'whitelist': {
const ret = await whitelistIP(ip);
if (ret) {
await cleanCacheForIP(ip);

View File

@ -94,14 +94,12 @@ async function dummy() {
return [false, 'dummy'];
}
async function saveIPInfo(ip, whoisRet, isProxy, info) {
const whoisData = whoisRet || {};
async function saveIPInfo(ip, whoisRet, allowed, info) {
try {
IPInfo.upsert({
await IPInfo.upsert({
...whoisRet,
ip,
...whoisData,
isProxy,
proxy: allowed,
pcheck: info,
});
} catch (error) {
@ -136,9 +134,9 @@ async function withoutCache(f, ip) {
allowed = !allowed;
status = (allowed) ? 0 : 1;
}
whoisRet = await whois(ip);
whoisRet = await whois(ip) || {};
} finally {
await saveIPInfo(ipKey, whoisRet, allowed, pcInfo);
await saveIPInfo(ipKey, whoisRet, status, pcInfo);
}
return {
@ -176,16 +174,18 @@ async function withCache(f, ip) {
if (checking.indexOf(ipKey) === -1 && lock > 0) {
lock -= 1;
checking.push(ipKey);
try {
const result = await withoutCache(f, ip);
cacheAllowed(ip, result);
} catch (error) {
logger.error('Error %s', error.message || error);
} finally {
const pos = checking.indexOf(ipKey);
if (~pos) checking.splice(pos, 1);
lock += 1;
}
withoutCache(f, ip)
.then((result) => {
cacheAllowed(ipKey, result);
})
.catch((error) => {
logger.error('Error %s', error.message || error);
})
.finally(() => {
const pos = checking.indexOf(ipKey);
if (~pos) checking.splice(pos, 1);
lock += 1;
});
}
return {
allowed: true,

View File

@ -249,15 +249,13 @@ export function durationToString(
export function largeDurationToString(
ts,
) {
let restA = Math.round(ts / 1000);
let restB = restA % (3600 * 24);
const days = restA - restB;
restA = restB % 3600;
const hours = restB - restA;
restB = restA % 60;
const minutes = restA - restB;
restA = restB % 60;
const seconds = restB - restA;
const seconds = ts % 60;
let durs = (ts - seconds) / 60;
const minutes = durs % 60;
durs = (durs - minutes) / 60;
const hours = durs % 24;
durs = (durs - hours) / 24;
const days = durs;
let out = '';
if (days) {
out += ` ${days}d`;

View File

@ -1,9 +1,11 @@
import { DataTypes } from 'sequelize';
import { DataTypes, Op } from 'sequelize';
import sequelize from './sequelize';
import RegUser from './RegUser';
import { HourlyCron } from '../../utils/cron';
import { cleanCacheForIP } from '../redis/isAllowedCache';
const Ban = sequelize.define('Blacklist', {
const Ban = sequelize.define('Ban', {
ip: {
type: DataTypes.CHAR(39),
allowNull: false,
@ -41,7 +43,38 @@ const Ban = sequelize.define('Blacklist', {
},
});
Ban.belongsTo(RegUser, {
as: 'mod',
foreignKey: 'muid',
});
async function cleanBans() {
const expiredIPs = await Ban.findAll({
attributes: [
'ip',
],
where: {
expires: {
[Op.lte]: new Date(),
},
},
raw: true,
});
const ips = [];
for (let i = 0; i < expiredIPs.length; i += 1) {
ips.push(expiredIPs[i].ip);
}
await Ban.destroy({
where: {
ip: ips,
},
});
for (let i = 0; i < ips.length; i += 1) {
// eslint-disable-next-line no-await-in-loop
await cleanCacheForIP(ips[i]);
}
}
HourlyCron.hook(cleanBans);
/*
* check if ip is whitelisted
@ -61,16 +94,20 @@ export async function isIPBanned(ip) {
* @param ip
* @return
*/
export async function getBanInfo(ip) {
const ban = await Ban.findByPk(ip, {
include: [{
export function getBanInfo(ip) {
return Ban.findByPk(ip, {
attributes: ['reason', 'expires'],
include: {
model: RegUser,
as: 'mod',
foreignKey: 'muid',
attributes: ['id', 'name'],
}],
attributes: [
'id',
'name',
],
},
raw: true,
nest: true,
});
return ban;
}
/*
@ -85,15 +122,14 @@ export async function banIP(
expiresTs,
muid,
) {
const expires = (expiresTs) ? new Date(expiresTs) : null;
const [, created] = await Ban.findOrCreate({
where: { ip },
defaults: {
reason,
expires,
muid,
},
const expires = (expiresTs) ? new Date(expiresTs).toISOString() : null;
const [, created] = await Ban.upsert({
ip,
reason,
expires,
muid,
});
await cleanCacheForIP(ip);
return created;
}
@ -107,6 +143,7 @@ export async function unbanIP(ip) {
const count = await Ban.destroy({
where: { ip },
});
await cleanCacheForIP(ip);
return !!count;
}

View File

@ -80,7 +80,9 @@ const IPInfo = sequelize.define('IPInfo', {
},
pcheck(value) {
this.setDataValue('pcheck', value.slice(0, 60));
if (value) {
this.setDataValue('pcheck', value.slice(0, 60));
}
},
country(value) {
@ -101,12 +103,25 @@ export async function getIPofIID(uuid) {
raw: true,
});
} catch {
// nothing
}
return result && result.ip;
}
export async function getIIDofIP(ip) {
if (!ip) {
return null;
}
if (result) {
return result.ip;
let result = null;
try {
result = await IPInfo.findByPk(ip, {
attributes: ['uuid'],
raw: true,
});
} catch {
// nothing
}
return null;
return result && result.uuid;
}
export async function getIdsToIps(ips) {

View File

@ -103,12 +103,4 @@ router.post('/local', passport.authenticate('json'), async (req, res) => {
});
});
// eslint-disable-next-line no-unused-vars
router.use((err, req, res, next) => {
res.status(400);
res.json({
errors: [err.message],
});
});
export default router;

45
src/routes/api/baninfo.js Normal file
View File

@ -0,0 +1,45 @@
/*
*
*/
import {
getIPFromRequest,
getIPv6Subnet,
} from '../../utils/ip';
import {
getBanInfo,
unbanIP,
} from '../../data/sql/Ban';
async function baninfo(req, res, next) {
try {
const { t } = req.ttag;
const ip = getIPv6Subnet(
getIPFromRequest(req),
);
const info = await getBanInfo(ip);
if (!info) {
throw new Error(t`You are not banned`);
}
let sleft = (info.expires)
? Math.round((info.expires.getTime() - Date.now()) / 1000)
: 0;
if (info.expires && sleft < 3) {
await unbanIP(ip);
sleft = -1;
}
res.status(200).json({
reason: info.reason,
sleft,
mod: `${info.mod.name} (${info.mod.id})`,
});
} catch (err) {
next(err);
}
}
export default baninfo;

32
src/routes/api/getiid.js Normal file
View File

@ -0,0 +1,32 @@
/*
*
*/
import {
getIPFromRequest,
getIPv6Subnet,
} from '../../utils/ip';
import {
getIIDofIP,
} from '../../data/sql/IPInfo';
async function getiid(req, res, next) {
try {
const ip = getIPv6Subnet(
getIPFromRequest(req),
);
const iid = await getIIDofIP(ip);
if (!iid) {
throw new Error('Could not get IID');
}
res.status(200).json({
iid,
});
} catch (err) {
next(err);
}
}
export default getiid;

View File

@ -15,24 +15,26 @@ import leaveChan from './leavechan';
import block from './block';
import blockdm from './blockdm';
import modtools from './modtools';
import baninfo from './baninfo';
import getiid from './getiid';
const router = express.Router();
router.use(express.json());
// eslint-disable-next-line no-unused-vars
router.use((err, req, res, next) => {
if (err) {
logger.warn(`Got invalid json from ${req.trueIp} on ${req.originalUrl}`);
res.status(400);
res.status(400).json({ errors: [{ msg: 'Invalid Request' }] });
} else {
next();
}
logger.warn(`Got invalid json from ${req.trueIp} on ${req.originalUrl}`);
res.status(400).json({
errors: [{ msg: 'Invalid Request' }],
});
});
// captcah doesn't need a user
// routes that don't need a user
router.post('/captcha', captcha);
router.get('/baninfo', baninfo);
router.get('/getiid', getiid);
/*
* get user session
@ -87,4 +89,11 @@ router.get('/me', me);
router.use('/auth', auth);
// eslint-disable-next-line no-unused-vars
router.use((err, req, res, next) => {
res.status(400).json({
errors: [err.message],
});
});
export default router;

View File

@ -89,103 +89,112 @@ router.post('/', upload.single('image'), async (req, res, next) => {
);
};
if (req.body.cleanerstat) {
const ret = CanvasCleaner.reportStatus();
res.status(200);
res.json(ret);
return;
const bLogger = (text) => {
logger.info(`IID> ${req.user.regUser.name}[${req.user.id}]> ${text}`);
};
try {
if (req.body.cleanerstat) {
const ret = CanvasCleaner.reportStatus();
res.status(200);
res.json(ret);
return;
}
if (req.body.cleanercancel) {
const ret = CanvasCleaner.stop();
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.iidaction) {
const {
iidaction, iid, reason, time,
} = req.body;
const ret = await executeIIDAction(
iidaction,
iid,
reason,
time,
req.user.id,
bLogger,
);
res.status(200).send(ret);
return;
}
if (req.body.cleaneraction) {
const {
cleaneraction, ulcoor, brcoor, canvasid,
} = req.body;
const [ret, msg] = await executeCleanerAction(
cleaneraction,
ulcoor,
brcoor,
canvasid,
aLogger,
);
res.status(ret).send(msg);
return;
}
if (req.body.imageaction) {
const { imageaction, coords, canvasid } = req.body;
const [ret, msg] = await executeImageAction(
imageaction,
req.file,
coords,
canvasid,
aLogger,
);
res.status(ret).send(msg);
return;
}
if (req.body.protaction) {
const {
protaction, ulcoor, brcoor, canvasid,
} = req.body;
const [ret, msg] = await executeProtAction(
protaction,
ulcoor,
brcoor,
canvasid,
aLogger,
);
res.status(ret).send(msg);
return;
}
if (req.body.rollback) {
// rollback is date as YYYYMMdd
const {
rollback, ulcoor, brcoor, canvasid,
} = req.body;
const [ret, msg] = await executeRollback(
rollback,
ulcoor,
brcoor,
canvasid,
aLogger,
(req.user.userlvl === 1),
);
res.status(ret).send(msg);
return;
}
next();
} catch (err) {
next(err);
}
if (req.body.cleanercancel) {
const ret = CanvasCleaner.stop();
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.iidaction) {
const {
iidaction, iid, reason, time,
} = req.body;
const ret = await executeIIDAction(
iidaction,
iid,
reason,
time,
req.user.id,
);
res.status(200).send(ret);
return;
}
if (req.body.cleaneraction) {
const {
cleaneraction, ulcoor, brcoor, canvasid,
} = req.body;
const [ret, msg] = await executeCleanerAction(
cleaneraction,
ulcoor,
brcoor,
canvasid,
aLogger,
);
res.status(ret).send(msg);
return;
}
if (req.body.imageaction) {
const { imageaction, coords, canvasid } = req.body;
const [ret, msg] = await executeImageAction(
imageaction,
req.file,
coords,
canvasid,
aLogger,
);
res.status(ret).send(msg);
return;
}
if (req.body.protaction) {
const {
protaction, ulcoor, brcoor, canvasid,
} = req.body;
const [ret, msg] = await executeProtAction(
protaction,
ulcoor,
brcoor,
canvasid,
aLogger,
);
res.status(ret).send(msg);
return;
}
if (req.body.rollback) {
// rollback is date as YYYYMMdd
const {
rollback, ulcoor, brcoor, canvasid,
} = req.body;
const [ret, msg] = await executeRollback(
rollback,
ulcoor,
brcoor,
canvasid,
aLogger,
(req.user.userlvl === 1),
);
res.status(ret).send(msg);
return;
}
next();
});
@ -209,33 +218,37 @@ router.post('/', async (req, res, next) => {
logger.info(`ADMIN> ${req.user.regUser.name}[${req.user.id}]> ${text}`);
};
if (req.body.ipaction) {
const ret = await executeIPAction(
req.body.ipaction,
req.body.ip,
aLogger,
);
res.status(200).send(ret);
return;
try {
if (req.body.ipaction) {
const ret = await executeIPAction(
req.body.ipaction,
req.body.ip,
aLogger,
);
res.status(200).send(ret);
return;
}
if (req.body.modlist) {
const ret = await getModList();
res.status(200);
res.json(ret);
return;
}
if (req.body.remmod) {
const ret = await removeMod(req.body.remmod);
res.status(200).send(ret);
return;
}
if (req.body.makemod) {
const ret = await makeMod(req.body.makemod);
res.status(200);
res.json(ret);
return;
}
next();
} catch (err) {
next(err);
}
if (req.body.modlist) {
const ret = await getModList();
res.status(200);
res.json(ret);
return;
}
if (req.body.remmod) {
const ret = await removeMod(req.body.remmod);
res.status(200).send(ret);
return;
}
if (req.body.makemod) {
const ret = await makeMod(req.body.makemod);
res.status(200);
res.json(ret);
return;
}
next();
});
router.use(async (req, res) => {

View File

@ -491,20 +491,21 @@ class SocketServer {
if (await needCaptcha(ip)) {
// need captcha
failureRet = PixelReturn.dehydrate(10, 0, 0);
}
// (re)check for Proxy
const allowed = await isIPAllowed(ip);
if (!allowed.allowed) {
// proxy
let failureStatus = 11;
if (allowed.status === 2) {
// banned
failureStatus = 14;
} else if (allowed.status === 3) {
// range banned
failureStatus = 15;
} else {
// (re)check for Proxy
const allowed = await isIPAllowed(ip);
if (!allowed.allowed) {
// proxy
let failureStatus = 11;
if (allowed.status === 2) {
// banned
failureStatus = 14;
} else if (allowed.status === 3) {
// range banned
failureStatus = 15;
}
failureRet = PixelReturn.dehydrate(failureStatus, 0, 0);
}
failureRet = PixelReturn.dehydrate(failureStatus, 0, 0);
}
if (failureRet !== null) {
const now = Date.now();

View File

@ -41,7 +41,6 @@ export type Action =
| { type: 'SELECT_CANVAS', canvasId: number }
| { type: 'PLACED_PIXELS', amount: number }
| { type: 'PIXEL_WAIT' }
| { type: 'PIXEL_FAILURE' }
| { type: 'SET_VIEW_COORDINATES', view: Array }
| { type: 'SET_SCALE', scale: number, zoompoint: Array }
| { type: 'REQUEST_BIG_CHUNK', center: Array }

View File

@ -293,3 +293,9 @@ export function requestMe() {
'api/me',
);
}
export function requestIID() {
return makeAPIGETRequest(
'api/getiid',
);
}

View File

@ -115,6 +115,10 @@ export function toggleOpenMenu() {
};
}
/*
* requestingPixel is inveted, it has the meaning of
* "can i request a pixel"
*/
export function setRequestingPixel(requestingPixel) {
return {
type: 'SET_REQUESTING_PIXEL',
@ -195,12 +199,6 @@ export function pixelWait() {
};
}
export function pixelFailure() {
return {
type: 'PIXEL_FAILURE',
};
}
export function receiveOnline(online) {
return {
type: 'RECEIVE_ONLINE',

View File

@ -94,7 +94,10 @@ export default (store) => (next) => (action) => {
break;
}
case 'PIXEL_FAILURE': {
case 'ALERT': {
if (action.alertType !== 'error') {
break;
}
const oscillatorNode = context.createOscillator();
const gainNode = context.createGain();

View File

@ -10,7 +10,6 @@ import {
setRequestingPixel,
pAlert,
gotCoolDownDelta,
pixelFailure,
setWait,
placedPixels,
pixelWait,
@ -187,6 +186,7 @@ export function receivePixelReturn(
let errorTitle = null;
let msg = null;
let type = 'error';
switch (retCode) {
case 0:
store.dispatch(placedPixels(pxlCnt));
@ -228,13 +228,10 @@ export function receivePixelReturn(
store.dispatch(pixelWait());
break;
case 10:
store.dispatch(pAlert(
'Captcha',
t`Please prove that you are human`,
'captcha',
));
store.dispatch(setRequestingPixel(true));
return;
errorTitle = 'Captcha';
msg = t`Please prove that you are human`;
type = 'captcha';
break;
case 11:
errorTitle = t`No Proxies Allowed :(`;
msg = t`You are using a Proxy.`;
@ -249,13 +246,9 @@ export function receivePixelReturn(
msg = t`Server got confused by your pixels. Are you playing on multiple devices?`;
break;
case 14:
store.dispatch(pAlert(
'Banned',
t`You are banned.`,
'ban',
));
store.dispatch(setRequestingPixel(true));
return;
errorTitle = t`Banned`;
type = t`ban`;
break;
case 15:
errorTitle = t`Range Banned`;
msg = t`Your Internet Provider is banned from playing this game`;
@ -265,17 +258,19 @@ export function receivePixelReturn(
msg = t`Couldn't set Pixel`;
}
if (msg) {
store.dispatch(pixelFailure());
if (msg || errorTitle) {
store.dispatch(pAlert(
(errorTitle || t`Error ${retCode}`),
msg,
'error',
type,
));
}
store.dispatch(setRequestingPixel(true));
/* start next request if queue isn't empty */
requestFromQueue(store);
if (!msg) {
/* start next request if queue isn't empty */
requestFromQueue(store);
}
}

View File

@ -31,9 +31,9 @@ class Cron {
}
checkForExecution() {
this.timeout = setTimeout(this.checkForExecution, HOUR);
const curDate = new Date();
const curTime = curDate.getTime();
this.timeout = setTimeout(this.checkForExecution, HOUR);
if (curTime + 120000 > this.lastRun + this.interval * HOUR) {
// eslint-disable-next-line max-len
logger.info(`${curDate.toUTCString()}> Run cron events for interval: ${this.interval}h`);
@ -65,7 +65,7 @@ function initializeDailyCron() {
}
function initializeHourlyCron() {
const cron = new Cron(1, Date.now());
const cron = new Cron(1);
return cron;
}