make captcha more general and add captcha to signup form
This commit is contained in:
parent
2bc1aa9591
commit
c29578dfaf
|
@ -251,10 +251,12 @@ export function requestLogin(nameoremail, password) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function requestRegistration(name, email, password) {
|
export function requestRegistration(name, email, password, captcha, captchaid) {
|
||||||
return makeAPIPOSTRequest(
|
return makeAPIPOSTRequest(
|
||||||
'api/auth/register',
|
'api/auth/register',
|
||||||
{ name, email, password },
|
{
|
||||||
|
name, email, password, captcha, captchaid,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
import React, { useState, useEffect, useCallback } from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
import { useSelector, useDispatch } from 'react-redux';
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
|
|
||||||
import Captcha from './Captcha';
|
import GlobalCaptcha from './GlobalCaptcha';
|
||||||
import { closeAlert } from '../actions';
|
import { closeAlert } from '../actions';
|
||||||
|
|
||||||
const Alert = () => {
|
const Alert = () => {
|
||||||
|
@ -55,7 +55,7 @@ const Alert = () => {
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
{(alertType === 'captcha')
|
{(alertType === 'captcha')
|
||||||
? <Captcha close={close} />
|
? <GlobalCaptcha close={close} />
|
||||||
: (
|
: (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Form to ask for captcha.
|
* Form to ask for captcha.
|
||||||
* If callback is provided, it sets the captcha text to it.
|
* Offers input for captchas, parent needs to provide a form and
|
||||||
* If callback is not provided, it provides a button to send the
|
* get "captcha" and "captchaid" values
|
||||||
* captcha itself
|
|
||||||
* @flow
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable jsx-a11y/no-autofocus */
|
/* eslint-disable jsx-a11y/no-autofocus */
|
||||||
|
@ -12,7 +10,6 @@ import React, { useState, useEffect } from 'react';
|
||||||
import { t } from 'ttag';
|
import { t } from 'ttag';
|
||||||
|
|
||||||
import { IoReloadCircleSharp } from 'react-icons/io5';
|
import { IoReloadCircleSharp } from 'react-icons/io5';
|
||||||
import { requestSolveCaptcha } from '../actions/fetch';
|
|
||||||
|
|
||||||
async function getUrlAndId() {
|
async function getUrlAndId() {
|
||||||
const url = window.ssv.captchaurl;
|
const url = window.ssv.captchaurl;
|
||||||
|
@ -27,40 +24,38 @@ async function getUrlAndId() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Captcha = ({ callback, close }) => {
|
/*
|
||||||
|
* autoload: Load captcha immediately and autofocus input textbox
|
||||||
|
* width: width of the captcha image
|
||||||
|
*/
|
||||||
|
const Captcha = ({ autoload, width }) => {
|
||||||
const [captchaData, setCaptchaData] = useState({});
|
const [captchaData, setCaptchaData] = useState({});
|
||||||
const [text, setText] = useState('');
|
|
||||||
const [errors, setErrors] = useState([]);
|
const [errors, setErrors] = useState([]);
|
||||||
const [imgLoaded, setImgLoaded] = useState(false);
|
const [imgLoaded, setImgLoaded] = useState(false);
|
||||||
|
|
||||||
useEffect(async () => {
|
const reloadCaptcha = async () => {
|
||||||
const [svgUrl, captchaid] = await getUrlAndId();
|
if (imgLoaded) {
|
||||||
|
setImgLoaded(false);
|
||||||
|
}
|
||||||
|
const captchaResponse = await getUrlAndId();
|
||||||
|
if (!captchaResponse) {
|
||||||
|
setErrors([t`Could not load captcha`]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const [svgUrl, captchaid] = captchaResponse;
|
||||||
setCaptchaData({ url: svgUrl, id: captchaid });
|
setCaptchaData({ url: svgUrl, id: captchaid });
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(async () => {
|
||||||
|
if (autoload) {
|
||||||
|
reloadCaptcha();
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const contWidth = width || 100;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<>
|
||||||
onSubmit={async (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
const { errors: resErrors } = await requestSolveCaptcha(
|
|
||||||
text,
|
|
||||||
captchaData.id,
|
|
||||||
);
|
|
||||||
if (resErrors) {
|
|
||||||
const [svgUrl, captchaid] = await getUrlAndId();
|
|
||||||
setCaptchaData({ url: svgUrl, id: captchaid });
|
|
||||||
setText('');
|
|
||||||
setErrors(resErrors);
|
|
||||||
} else {
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{errors.map((error) => (
|
|
||||||
<p key={error} className="errormessage">
|
|
||||||
<span>{t`Error`}</span>: {error}
|
|
||||||
</p>
|
|
||||||
))}
|
|
||||||
<p className="modaltext">
|
<p className="modaltext">
|
||||||
{t`Type the characters from the following image:`}
|
{t`Type the characters from the following image:`}
|
||||||
|
|
||||||
|
@ -69,30 +64,61 @@ const Captcha = ({ callback, close }) => {
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<br />
|
<br />
|
||||||
|
{errors.map((error) => (
|
||||||
|
<p key={error} className="errormessage">
|
||||||
|
<span>{t`Error`}</span>: {error}
|
||||||
|
</p>
|
||||||
|
))}
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: `${contWidth}%`,
|
||||||
paddingTop: '60%',
|
paddingTop: `${Math.floor(contWidth * 0.6)}%`,
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
|
display: 'inline-block',
|
||||||
|
backgroundColor: '#e0e0e0',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{(captchaData.url) && (
|
<div
|
||||||
<img
|
style={{
|
||||||
style={{
|
width: '100%',
|
||||||
width: '100%',
|
position: 'absolute',
|
||||||
position: 'absolute',
|
top: '50%',
|
||||||
top: '50%',
|
left: '50%',
|
||||||
left: '50%',
|
transform: 'translate(-50%,-50%)',
|
||||||
opacity: (imgLoaded) ? 1 : 0,
|
}}
|
||||||
transform: 'translate(-50%,-50%)',
|
>
|
||||||
transition: '100ms',
|
{(captchaData.url)
|
||||||
}}
|
? (
|
||||||
src={captchaData.url}
|
<img
|
||||||
alt="CAPTCHA"
|
style={{
|
||||||
onLoad={() => { setImgLoaded(true); }}
|
width: '100%',
|
||||||
onError={() => setErrors([t`Could not load captcha`])}
|
opacity: (imgLoaded) ? 1 : 0,
|
||||||
/>
|
transition: '100ms',
|
||||||
)}
|
}}
|
||||||
|
src={captchaData.url}
|
||||||
|
alt="CAPTCHA"
|
||||||
|
onLoad={() => {
|
||||||
|
setErrors([]);
|
||||||
|
setImgLoaded(true);
|
||||||
|
}}
|
||||||
|
onError={() => {
|
||||||
|
setErrors([t`Could not load captcha`]);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<span
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
title={t`Load Captcha`}
|
||||||
|
className="modallink"
|
||||||
|
onClick={reloadCaptcha}
|
||||||
|
onKeyPress={reloadCaptcha}
|
||||||
|
>
|
||||||
|
{t`Click to Load Captcha`}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="modaltext">
|
<p className="modaltext">
|
||||||
{t`Can't read? Reload:`}
|
{t`Can't read? Reload:`}
|
||||||
|
@ -102,54 +128,29 @@ const Captcha = ({ callback, close }) => {
|
||||||
title={t`Reload`}
|
title={t`Reload`}
|
||||||
className="modallink"
|
className="modallink"
|
||||||
style={{ fontSize: 28 }}
|
style={{ fontSize: 28 }}
|
||||||
onClick={async () => {
|
onClick={reloadCaptcha}
|
||||||
setImgLoaded(false);
|
|
||||||
const [svgUrl, captchaid] = await getUrlAndId();
|
|
||||||
setCaptchaData({ url: svgUrl, id: captchaid });
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<IoReloadCircleSharp />
|
<IoReloadCircleSharp />
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<input
|
<input
|
||||||
|
name="captcha"
|
||||||
placeholder={t`Enter Characters`}
|
placeholder={t`Enter Characters`}
|
||||||
type="text"
|
type="text"
|
||||||
value={text}
|
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
autoCorrect="off"
|
autoCorrect="off"
|
||||||
autoCapitalize="off"
|
autoCapitalize="off"
|
||||||
spellCheck="false"
|
spellCheck="false"
|
||||||
autoFocus
|
autoFocus={autoload}
|
||||||
style={{
|
style={{
|
||||||
width: '6em',
|
width: '6em',
|
||||||
fontSize: 21,
|
fontSize: 21,
|
||||||
margin: 5,
|
margin: 5,
|
||||||
}}
|
}}
|
||||||
onChange={(evt) => {
|
|
||||||
const txt = evt.target.value;
|
|
||||||
setText(txt);
|
|
||||||
if (callback) callback(txt);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
{(!callback) && (
|
<input type="hidden" name="captchaid" value={captchaData.id || '0'} />
|
||||||
<div>
|
<br />
|
||||||
<button
|
</>
|
||||||
type="button"
|
|
||||||
onClick={close}
|
|
||||||
style={{ fontSize: 16 }}
|
|
||||||
>
|
|
||||||
{t`Cancel`}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
style={{ fontSize: 16 }}
|
|
||||||
>
|
|
||||||
{t`Send`}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</form>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
62
src/components/GlobalCaptcha.jsx
Normal file
62
src/components/GlobalCaptcha.jsx
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Global Captcha that is valid sitewide
|
||||||
|
* via api/captcha
|
||||||
|
* Displayed in an Alert
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { t } from 'ttag';
|
||||||
|
|
||||||
|
import Captcha from './Captcha';
|
||||||
|
import { requestSolveCaptcha } from '../actions/fetch';
|
||||||
|
|
||||||
|
const GlobalCaptcha = ({ close }) => {
|
||||||
|
const [errors, setErrors] = useState([]);
|
||||||
|
// used to be able to force Captcha rerender on error
|
||||||
|
const [captKey, setCaptKey] = useState(Date.now());
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form
|
||||||
|
onSubmit={async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const text = e.target.captcha.value;
|
||||||
|
const captchaid = e.target.captchaid.value;
|
||||||
|
const { errors: resErrors } = await requestSolveCaptcha(
|
||||||
|
text,
|
||||||
|
captchaid,
|
||||||
|
);
|
||||||
|
if (resErrors) {
|
||||||
|
setCaptKey(Date.now());
|
||||||
|
setErrors(resErrors);
|
||||||
|
} else {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{errors.map((error) => (
|
||||||
|
<p key={error} className="errormessage">
|
||||||
|
<span>{t`Error`}</span>: {error}
|
||||||
|
</p>
|
||||||
|
))}
|
||||||
|
<Captcha autoload key={captKey} />
|
||||||
|
<p>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={close}
|
||||||
|
style={{ fontSize: 16 }}
|
||||||
|
>
|
||||||
|
{t`Cancel`}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
style={{ fontSize: 16 }}
|
||||||
|
>
|
||||||
|
{t`Send`}
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(GlobalCaptcha);
|
|
@ -6,6 +6,7 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import { t } from 'ttag';
|
import { t } from 'ttag';
|
||||||
|
import Captcha from '../Captcha';
|
||||||
import {
|
import {
|
||||||
validateEMail, validateName, validatePassword,
|
validateEMail, validateName, validatePassword,
|
||||||
} from '../../utils/validation';
|
} from '../../utils/validation';
|
||||||
|
@ -29,27 +30,27 @@ function validate(name, email, password, confirmPassword) {
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
const inputStyles = {
|
|
||||||
display: 'inline-block',
|
|
||||||
width: '100%',
|
|
||||||
maxWidth: '35em',
|
|
||||||
};
|
|
||||||
|
|
||||||
const Register = ({ windowId }) => {
|
const Register = ({ windowId }) => {
|
||||||
const [name, setName] = useState('');
|
|
||||||
const [email, setEmail] = useState('');
|
|
||||||
const [password, setPassword] = useState('');
|
|
||||||
const [confirmPassword, setConfirmPassword] = useState('');
|
|
||||||
const [submitting, setSubmitting] = useState('');
|
const [submitting, setSubmitting] = useState('');
|
||||||
const [errors, setErrors] = useState([]);
|
const [errors, setErrors] = useState([]);
|
||||||
|
// used to be able to force Captcha rerender on error
|
||||||
|
const [captKey, setCaptKey] = useState(Date.now());
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async (evt) => {
|
||||||
|
evt.preventDefault();
|
||||||
if (submitting) {
|
if (submitting) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const name = evt.target.name.value;
|
||||||
|
const email = evt.target.email.value;
|
||||||
|
const password = evt.target.password.value;
|
||||||
|
const confirmPassword = evt.target.confirmpassword.value;
|
||||||
|
const captcha = evt.target.captcha.value;
|
||||||
|
const captchaid = evt.target.captchaid.value;
|
||||||
|
|
||||||
const valErrors = validate(name, email, password, confirmPassword);
|
const valErrors = validate(name, email, password, confirmPassword);
|
||||||
if (valErrors.length > 0) {
|
if (valErrors.length > 0) {
|
||||||
setErrors(valErrors);
|
setErrors(valErrors);
|
||||||
|
@ -61,9 +62,12 @@ const Register = ({ windowId }) => {
|
||||||
name,
|
name,
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
|
captcha,
|
||||||
|
captchaid,
|
||||||
);
|
);
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
if (respErrors) {
|
if (respErrors) {
|
||||||
|
setCaptKey(Date.now());
|
||||||
setErrors(respErrors);
|
setErrors(respErrors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -83,38 +87,40 @@ const Register = ({ windowId }) => {
|
||||||
<p key={error} className="errormessage"><span>{t`Error`}</span>
|
<p key={error} className="errormessage"><span>{t`Error`}</span>
|
||||||
: {error}</p>
|
: {error}</p>
|
||||||
))}
|
))}
|
||||||
|
<p className="modaltitle">{t`Name`}:</p>
|
||||||
<input
|
<input
|
||||||
style={inputStyles}
|
name="name"
|
||||||
value={name}
|
className="reginput"
|
||||||
autoComplete="username"
|
autoComplete="username"
|
||||||
onChange={(evt) => setName(evt.target.value)}
|
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={t`Name`}
|
placeholder={t`Name`}
|
||||||
/><br />
|
/>
|
||||||
|
<p className="modaltitle">{t`Email`}:</p>
|
||||||
<input
|
<input
|
||||||
style={inputStyles}
|
name="email"
|
||||||
value={email}
|
className="reginput"
|
||||||
autoComplete="email"
|
autoComplete="email"
|
||||||
onChange={(evt) => setEmail(evt.target.value)}
|
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={t`Email`}
|
placeholder={t`Email`}
|
||||||
/><br />
|
/>
|
||||||
|
<p className="modaltitle">{t`Password`}:</p>
|
||||||
<input
|
<input
|
||||||
style={inputStyles}
|
name="password"
|
||||||
value={password}
|
className="reginput"
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
onChange={(evt) => setPassword(evt.target.value)}
|
|
||||||
type="password"
|
type="password"
|
||||||
placeholder={t`Password`}
|
placeholder={t`Password`}
|
||||||
/><br />
|
/>
|
||||||
|
<p className="modaltitle">{t`Confirm Password`}:</p>
|
||||||
<input
|
<input
|
||||||
style={inputStyles}
|
name="confirmpassword"
|
||||||
value={confirmPassword}
|
className="reginput"
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
onChange={(evt) => setConfirmPassword(evt.target.value)}
|
|
||||||
type="password"
|
type="password"
|
||||||
placeholder={t`Confirm Password`}
|
placeholder={t`Confirm Password`}
|
||||||
/><br />
|
/>
|
||||||
|
<p className="modaltitle">{t`Captcha`}:</p>
|
||||||
|
<Captcha autoload={false} width={60} key={captKey} />
|
||||||
<button type="submit">
|
<button type="submit">
|
||||||
{(submitting) ? '...' : t`Submit`}
|
{(submitting) ? '...' : t`Submit`}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -17,8 +17,11 @@ import {
|
||||||
validateName,
|
validateName,
|
||||||
validatePassword,
|
validatePassword,
|
||||||
} from '../../../utils/validation';
|
} from '../../../utils/validation';
|
||||||
|
import {
|
||||||
|
checkCaptchaSolution,
|
||||||
|
} from '../../../utils/captcha';
|
||||||
|
|
||||||
async function validate(email, name, password, t, gettext) {
|
async function validate(email, name, password, captcha, captchaid, t, gettext) {
|
||||||
const errors = [];
|
const errors = [];
|
||||||
const emailerror = gettext(validateEMail(email));
|
const emailerror = gettext(validateEMail(email));
|
||||||
if (emailerror) errors.push(emailerror);
|
if (emailerror) errors.push(emailerror);
|
||||||
|
@ -27,6 +30,8 @@ async function validate(email, name, password, t, gettext) {
|
||||||
const passworderror = gettext(validatePassword(password));
|
const passworderror = gettext(validatePassword(password));
|
||||||
if (passworderror) errors.push(passworderror);
|
if (passworderror) errors.push(passworderror);
|
||||||
|
|
||||||
|
if (!captcha || !captchaid) errors.push(t`No Captcha given`);
|
||||||
|
|
||||||
let reguser = await RegUser.findOne({ where: { email } });
|
let reguser = await RegUser.findOne({ where: { email } });
|
||||||
if (reguser) errors.push(t`E-Mail already in use.`);
|
if (reguser) errors.push(t`E-Mail already in use.`);
|
||||||
reguser = await RegUser.findOne({ where: { name } });
|
reguser = await RegUser.findOne({ where: { name } });
|
||||||
|
@ -36,9 +41,34 @@ async function validate(email, name, password, t, gettext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async (req: Request, res: Response) => {
|
export default async (req: Request, res: Response) => {
|
||||||
const { email, name, password } = req.body;
|
const {
|
||||||
|
email, name, password, captcha, captchaid,
|
||||||
|
} = req.body;
|
||||||
const { t, gettext } = req.ttag;
|
const { t, gettext } = req.ttag;
|
||||||
const errors = await validate(email, name, password, t, gettext);
|
const errors = await validate(
|
||||||
|
email, name, password, captcha, captchaid, t, gettext,
|
||||||
|
);
|
||||||
|
|
||||||
|
const ip = getIPFromRequest(req);
|
||||||
|
if (!errors.length) {
|
||||||
|
const captchaPass = await checkCaptchaSolution(
|
||||||
|
captcha, ip, true, captchaid,
|
||||||
|
);
|
||||||
|
switch (captchaPass) {
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
errors.push(t`You took too long, try again.`);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
errors.push(t`You failed your captcha`);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
errors.push(t`Unknown Captcha Error`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
res.status(400);
|
res.status(400);
|
||||||
res.json({
|
res.json({
|
||||||
|
@ -63,7 +93,6 @@ export default async (req: Request, res: Response) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ip = getIPFromRequest(req);
|
|
||||||
logger.info(`Created new user ${name} ${email} ${ip}`);
|
logger.info(`Created new user ${name} ${email} ${ip}`);
|
||||||
|
|
||||||
const { user, lang } = req;
|
const { user, lang } = req;
|
||||||
|
|
|
@ -28,7 +28,7 @@ export default async (req: Request, res: Response) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ret = await checkCaptchaSolution(text, ip, id);
|
const ret = await checkCaptchaSolution(text, ip, false, id);
|
||||||
|
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
case 0:
|
case 0:
|
||||||
|
|
|
@ -805,6 +805,16 @@ tr:nth-child(even) {
|
||||||
background-color: #ffa9a9cc;
|
background-color: #ffa9a9cc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.reginput {
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 35em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.errormessage {
|
||||||
|
color: #b73c3c;
|
||||||
|
}
|
||||||
|
|
||||||
.cooldownbox {
|
.cooldownbox {
|
||||||
top: 16px;
|
top: 16px;
|
||||||
width: 48px;
|
width: 48px;
|
||||||
|
|
|
@ -90,6 +90,8 @@ export function setCaptchaSolution(
|
||||||
*
|
*
|
||||||
* @param text Solution of captcha
|
* @param text Solution of captcha
|
||||||
* @param ip
|
* @param ip
|
||||||
|
* @param onetime If the captcha is just one time or should be remembered
|
||||||
|
* for this ip
|
||||||
* @return 0 if solution right
|
* @return 0 if solution right
|
||||||
* 1 if timed out
|
* 1 if timed out
|
||||||
* 2 if wrong
|
* 2 if wrong
|
||||||
|
@ -97,6 +99,7 @@ export function setCaptchaSolution(
|
||||||
export async function checkCaptchaSolution(
|
export async function checkCaptchaSolution(
|
||||||
text,
|
text,
|
||||||
ip,
|
ip,
|
||||||
|
onetime = false,
|
||||||
captchaid = null,
|
captchaid = null,
|
||||||
) {
|
) {
|
||||||
const ipn = getIPv6Subnet(ip);
|
const ipn = getIPv6Subnet(ip);
|
||||||
|
@ -107,8 +110,10 @@ export async function checkCaptchaSolution(
|
||||||
const solution = await redis.getAsync(key);
|
const solution = await redis.getAsync(key);
|
||||||
if (solution) {
|
if (solution) {
|
||||||
if (evaluateResult(solution.toString('utf8'), text)) {
|
if (evaluateResult(solution.toString('utf8'), text)) {
|
||||||
const solvkey = `human:${ipn}`;
|
if (!onetime) {
|
||||||
await redis.setAsync(solvkey, '', 'EX', TTL_CACHE);
|
const solvkey = `human:${ipn}`;
|
||||||
|
await redis.setAsync(solvkey, '', 'EX', TTL_CACHE);
|
||||||
|
}
|
||||||
logger.info(`CAPTCHA ${ip} successfully solved captcha`);
|
logger.info(`CAPTCHA ${ip} successfully solved captcha`);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user