continue captchas

This commit is contained in:
HF 2021-03-17 11:00:58 +01:00
parent 2b9cae2ca2
commit 45e6d15fb3
9 changed files with 49 additions and 44 deletions

View File

@ -34,6 +34,17 @@ async function fetchWithTimeout(resource, options) {
async function parseAPIresponse(response) { async function parseAPIresponse(response) {
if (!response.ok) { if (!response.ok) {
const code = response.status; const code = response.status;
if (code === 429) {
let error = t`You made too many requests`;
const retryAfter = response.headers.get('Retry-After');
if (!Number.isNaN(Number(retryAfter))) {
const ti = Math.floor(retryAfter / 60);
error += `, ${t`try again after ${ti}min`}`;
}
return {
errors: [error],
};
}
return { return {
errors: [t`Connection error ${code} :(`], errors: [t`Connection error ${code} :(`],
}; };
@ -169,11 +180,17 @@ export async function requestLeaveChan(channelId: boolean) {
return t`Unknown Error`; return t`Unknown Error`;
} }
export function requestSolveCaptcha(text) { export async function requestSolveCaptcha(text) {
return makeAPIPOSTRequest( const res = await makeAPIPOSTRequest(
'api/captcha', 'api/captcha',
{ text }, { text },
); );
if (!res.errors && !res.success) {
return {
errors: [t`Server answered with gibberish :(`],
};
}
return res;
} }
export function requestPasswordChange(newPassword, password) { export function requestPasswordChange(newPassword, password) {

View File

@ -120,9 +120,9 @@ export function toggleOpenMenu(): Action {
}; };
} }
export function setPlaceAllowed(requestingPixel: boolean): Action { export function setRequestingPixel(requestingPixel: boolean): Action {
return { return {
type: 'SET_PLACE_ALLOWED', type: 'SET_REQUESTING_PIXEL',
requestingPixel, requestingPixel,
}; };
} }

View File

@ -29,7 +29,7 @@ export type Action =
| { type: 'SELECT_STYLE', style: string } | { type: 'SELECT_STYLE', style: string }
| { type: 'SET_NOTIFICATION', notification: string } | { type: 'SET_NOTIFICATION', notification: string }
| { type: 'UNSET_NOTIFICATION' } | { type: 'UNSET_NOTIFICATION' }
| { type: 'SET_PLACE_ALLOWED', requestingPixel: boolean } | { type: 'SET_REQUESTING_PIXEL', requestingPixel: boolean }
| { type: 'SET_HOVER', hover: Cell } | { type: 'SET_HOVER', hover: Cell }
| { type: 'UNSET_HOVER' } | { type: 'UNSET_HOVER' }
| { type: 'SET_WAIT', wait: ?number } | { type: 'SET_WAIT', wait: ?number }

View File

@ -56,7 +56,7 @@ const Alert = () => {
</p> </p>
<p> <p>
{(alertType === 'captcha') {(alertType === 'captcha')
? <Captcha cancel={close} /> ? <Captcha close={close} />
: ( : (
<button <button
type="button" type="button"

View File

@ -16,13 +16,18 @@ function getUrl() {
return `${window.ssv.captchaurl}/captcha.svg?${new Date().getTime()}`; return `${window.ssv.captchaurl}/captcha.svg?${new Date().getTime()}`;
} }
const Captcha = ({ callback, cancel }) => { const Captcha = ({ callback, close }) => {
const [captchaUrl, setCaptchaUrl] = useState(getUrl()); const [captchaUrl, setCaptchaUrl] = useState(getUrl());
const [text, setText] = useState(''); const [text, setText] = useState('');
const [error, setError] = useState(false); const [errors, setErrors] = useState([]);
return ( return (
<div> <div>
{errors.map((error) => (
<p key={error} className="errormessage">
<span>{t`Error`}</span>:&nbsp;{error}
</p>
))}
<p className="modaltext"> <p className="modaltext">
{t`Type the characters from the following image:`} {t`Type the characters from the following image:`}
<span style={{ fontSize: 11 }}> <span style={{ fontSize: 11 }}>
@ -32,7 +37,8 @@ const Captcha = ({ callback, cancel }) => {
<img <img
style={{ width: '75%' }} style={{ width: '75%' }}
src={captchaUrl} src={captchaUrl}
onError={() => setError(true)} alt="CAPTCHA"
onError={() => setErrors([t`Could not load captcha`])}
/> />
<p className="modaltext"> <p className="modaltext">
{t`Can't read? Reload:`}&nbsp; {t`Can't read? Reload:`}&nbsp;
@ -62,12 +68,21 @@ const Captcha = ({ callback, cancel }) => {
<div> <div>
<button <button
type="button" type="button"
onClick={cancel} onClick={close}
> >
{t`Cancel`} {t`Cancel`}
</button> </button>
<button <button
type="button" type="button"
onClick={async () => {
const { errors: resErrors } = await requestSolveCaptcha(text);
if (resErrors) {
setCaptchaUrl(getUrl());
setErrors(resErrors);
} else {
close();
}
}}
> >
{t`Send`} {t`Send`}
</button> </button>

View File

@ -183,25 +183,6 @@ class UserArea extends React.Component {
done={() => { this.setState({ socialSettingsExtended: false }); }} done={() => { this.setState({ socialSettingsExtended: false }); }}
/> />
)} )}
{(typeof window.hcaptcha !== 'undefined')
&& (
<img
role="presentation"
src="hcaptcha.svg"
alt="hCaptcha"
title="test hCaptcha"
onClick={() => {
window.pixel = null;
window.hcaptcha.execute();
}}
style={{
width: '5%',
height: '5%',
paddingTop: 20,
cursor: 'pointer',
}}
/>
)}
</p> </p>
); );
} }

View File

@ -68,7 +68,7 @@ export default function user(
}; };
} }
case 'SET_PLACE_ALLOWED': { case 'SET_REQUESTING_PIXEL': {
const { requestingPixel } = action; const { requestingPixel } = action;
return { return {
...state, ...state,

View File

@ -47,7 +47,7 @@ export default (store) => (next) => (action) => {
break; break;
} }
case 'SET_PLACE_ALLOWED': { case 'SET_REQUESTING_PIXEL': {
const renderer = getRenderer(); const renderer = getRenderer();
renderer.forceNextSubRender = true; renderer.forceNextSubRender = true;
break; break;

View File

@ -8,7 +8,7 @@
import { t } from 'ttag'; import { t } from 'ttag';
import { import {
notify, notify,
setPlaceAllowed, setRequestingPixel,
sweetAlert, sweetAlert,
gotCoolDownDelta, gotCoolDownDelta,
pixelFailure, pixelFailure,
@ -48,7 +48,7 @@ function requestFromQueue(store) {
pixelTimeout = setTimeout(() => { pixelTimeout = setTimeout(() => {
pixelQueue = []; pixelQueue = [];
pixelTimeout = null; pixelTimeout = null;
store.dispatch(setPlaceAllowed(true)); store.dispatch(setRequestingPixel(true));
store.dispatch(sweetAlert( store.dispatch(sweetAlert(
t`Error :(`, t`Error :(`,
t`Didn't get an answer from pixelplanet. Maybe try to refresh?`, t`Didn't get an answer from pixelplanet. Maybe try to refresh?`,
@ -60,16 +60,7 @@ function requestFromQueue(store) {
lastRequestValues = pixelQueue.shift(); lastRequestValues = pixelQueue.shift();
const { i, j, pixels } = lastRequestValues; const { i, j, pixels } = lastRequestValues;
ProtocolClient.requestPlacePixels(i, j, pixels); ProtocolClient.requestPlacePixels(i, j, pixels);
store.dispatch(setPlaceAllowed(false)); store.dispatch(setRequestingPixel(false));
// TODO:
// this is for resending after captcha returned
// window is ugly, put it into redux or something
window.pixel = {
i,
j,
pixels,
};
} }
export function receivePixelUpdate( export function receivePixelUpdate(
@ -245,6 +236,7 @@ export function receivePixelReturn(
'captcha', 'captcha',
t`OK`, t`OK`,
)); ));
store.dispatch(setRequestingPixel(true));
return; return;
case 11: case 11:
errorTitle = t`No Proxies Allowed :(`; errorTitle = t`No Proxies Allowed :(`;
@ -265,7 +257,7 @@ export function receivePixelReturn(
)); ));
} }
store.dispatch(setPlaceAllowed(true)); store.dispatch(setRequestingPixel(true));
/* start next request if queue isn't empty */ /* start next request if queue isn't empty */
requestFromQueue(store); requestFromQueue(store);
} }