The captcha now completes! Also made improvements in UI
This commit is contained in:
parent
20ab04f758
commit
23b611e093
|
@ -2,4 +2,5 @@ node_modules
|
|||
dist
|
||||
.env
|
||||
*.tsbuildinfo
|
||||
public/images
|
||||
public/images
|
||||
dev/mat.json
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>uCaptcha Test</title>
|
||||
<script src="../dist/client/bundle.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<form method="POST" action="/">
|
||||
<input type="email" name="email" placeholder="Email" />
|
||||
<input type="password" name="password" placeholder="Password" />
|
||||
|
||||
<div class="captcha-box"></div>
|
||||
|
||||
<input type="submit" value="Submit Form" />
|
||||
</form>
|
||||
|
||||
<script>
|
||||
ucaptcha.create("hUYUhuytryftyi6787yYtg67", ".captcha-box", function(token) {
|
||||
alert(token);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
684
dev/mat.json
684
dev/mat.json
|
@ -1,684 +0,0 @@
|
|||
{
|
||||
"OxXrEtvErYzN": {
|
||||
"mat": [
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.6,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.6,
|
||||
0.6,
|
||||
0.6,
|
||||
0.6,
|
||||
0.6
|
||||
],
|
||||
"nums": 1,
|
||||
"obj": 0
|
||||
},
|
||||
"zKsAXcOCNd9U": {
|
||||
"mat": [
|
||||
0.30000000000000004,
|
||||
0.5,
|
||||
0.30000000000000004,
|
||||
0.7,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004
|
||||
],
|
||||
"nums": 2,
|
||||
"obj": 0
|
||||
},
|
||||
"AyxF5UB7GyPC": {
|
||||
"mat": [
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.6,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4
|
||||
],
|
||||
"nums": 1,
|
||||
"obj": 0
|
||||
},
|
||||
"AD8Vjgv2zCjf": {
|
||||
"mat": [
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.6,
|
||||
0.6,
|
||||
0.6,
|
||||
0.6
|
||||
],
|
||||
"nums": 1,
|
||||
"obj": 0
|
||||
},
|
||||
"VEhAvRf5SlqP": {
|
||||
"mat": [
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.6,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.6,
|
||||
0.4,
|
||||
0.4,
|
||||
0.6,
|
||||
0.6
|
||||
],
|
||||
"nums": 1,
|
||||
"obj": 0
|
||||
},
|
||||
"REemIAxKWPOr": {
|
||||
"mat": [
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.5,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.5,
|
||||
0.5
|
||||
],
|
||||
"nums": 2,
|
||||
"obj": 0
|
||||
},
|
||||
"ilzrt3XTmEGI": {
|
||||
"mat": [
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.5,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.5,
|
||||
0.7
|
||||
],
|
||||
"nums": 2,
|
||||
"obj": 0
|
||||
},
|
||||
"sq8XFCoHMsjh": {
|
||||
"mat": [
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.4,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.4,
|
||||
0.6
|
||||
],
|
||||
"nums": 3,
|
||||
"obj": 0
|
||||
},
|
||||
"Rkb9KvzIK8i6": {
|
||||
"mat": [
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
0.20000000000000004,
|
||||
2.7755575615628914e-17,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.4
|
||||
],
|
||||
"nums": 5,
|
||||
"obj": 0
|
||||
},
|
||||
"zAQrZUdBtAul": {
|
||||
"mat": [
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
0.20000000000000004,
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
0.20000000000000004,
|
||||
0.4
|
||||
],
|
||||
"nums": 5,
|
||||
"obj": 0
|
||||
},
|
||||
"s0LDtVUcozVA": {
|
||||
"mat": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.10000000000000003,
|
||||
0,
|
||||
0,
|
||||
0.10000000000000003,
|
||||
0.30000000000000004
|
||||
],
|
||||
"nums": 6,
|
||||
"obj": 0
|
||||
},
|
||||
"Ern8By1vuGjV": {
|
||||
"mat": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.10000000000000003
|
||||
],
|
||||
"nums": 8,
|
||||
"obj": 0
|
||||
},
|
||||
"hn1uGROIKjnM": {
|
||||
"mat": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.10000000000000003
|
||||
],
|
||||
"nums": 8,
|
||||
"obj": 0
|
||||
},
|
||||
"4q7BkDtACkDx": {
|
||||
"mat": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2.7755575615628914e-17
|
||||
],
|
||||
"nums": 9,
|
||||
"obj": 0
|
||||
},
|
||||
"WlEm9ZGsDOlN": {
|
||||
"mat": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"nums": 10,
|
||||
"obj": 0
|
||||
},
|
||||
"4d6y7SDP9r9c": {
|
||||
"mat": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"nums": 11,
|
||||
"obj": 0
|
||||
},
|
||||
"AkGcL0NHOVGy": {
|
||||
"mat": [
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.5,
|
||||
0.5,
|
||||
0.5,
|
||||
0.5
|
||||
],
|
||||
"nums": 2,
|
||||
"obj": 0
|
||||
},
|
||||
"GHI69eo7oMwY": {
|
||||
"mat": [
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.5,
|
||||
0.5,
|
||||
0.7,
|
||||
0.7,
|
||||
0.7,
|
||||
0.7
|
||||
],
|
||||
"nums": 2,
|
||||
"obj": 0
|
||||
},
|
||||
"MkvjgP0uimhW": {
|
||||
"mat": [
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.4,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.4,
|
||||
0.4,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.6,
|
||||
0.6,
|
||||
0.6,
|
||||
0.6,
|
||||
0.7999999999999999,
|
||||
0.7999999999999999
|
||||
],
|
||||
"nums": 3,
|
||||
"obj": 0
|
||||
},
|
||||
"gFuaeUebn0lE": {
|
||||
"mat": [
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.5,
|
||||
0.5,
|
||||
0.5,
|
||||
0.30000000000000004,
|
||||
0.5,
|
||||
0.5,
|
||||
0.5
|
||||
],
|
||||
"nums": 2,
|
||||
"obj": 0
|
||||
},
|
||||
"xplUg1qXcCDV": {
|
||||
"mat": [
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.5,
|
||||
0.5,
|
||||
0.5,
|
||||
0.5,
|
||||
0.7,
|
||||
0.7,
|
||||
0.5
|
||||
],
|
||||
"nums": 2,
|
||||
"obj": 0
|
||||
},
|
||||
"KNRZDFuT4X45": {
|
||||
"mat": [
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.6,
|
||||
0.7999999999999999,
|
||||
0.6
|
||||
],
|
||||
"nums": 3,
|
||||
"obj": 0
|
||||
},
|
||||
"G68Efl2ZE0U1": {
|
||||
"mat": [
|
||||
0.10000000000000003,
|
||||
0.10000000000000003,
|
||||
0.10000000000000003,
|
||||
0.10000000000000003,
|
||||
0.10000000000000003,
|
||||
0.10000000000000003,
|
||||
0.10000000000000003,
|
||||
0.10000000000000003,
|
||||
0.10000000000000003,
|
||||
0.30000000000000004,
|
||||
0.5,
|
||||
0.5,
|
||||
0.30000000000000004,
|
||||
0.5,
|
||||
0.8999999999999999,
|
||||
0.7
|
||||
],
|
||||
"nums": 4,
|
||||
"obj": 0
|
||||
},
|
||||
"JOAT6FV7VKKi": {
|
||||
"mat": [
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
2.7755575615628914e-17,
|
||||
0.20000000000000004,
|
||||
0.4,
|
||||
0.4,
|
||||
0.20000000000000004,
|
||||
0.4,
|
||||
0.7999999999999999,
|
||||
0.6
|
||||
],
|
||||
"nums": 5,
|
||||
"obj": 0
|
||||
},
|
||||
"B25hZGGvUjZw": {
|
||||
"mat": [
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4
|
||||
],
|
||||
"nums": 1,
|
||||
"obj": 0
|
||||
},
|
||||
"mX0MuHDZJFVx": {
|
||||
"mat": [
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004,
|
||||
0.20000000000000004
|
||||
],
|
||||
"nums": 3,
|
||||
"obj": 0
|
||||
},
|
||||
"GuOSUS3pciGR": {
|
||||
"mat": [
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.6,
|
||||
0.6,
|
||||
0.4,
|
||||
0.6,
|
||||
0.6,
|
||||
0.6
|
||||
],
|
||||
"nums": 1,
|
||||
"obj": 0
|
||||
},
|
||||
"OaeOLRd0m1Nu": {
|
||||
"mat": [
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.5,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.5
|
||||
],
|
||||
"nums": 2,
|
||||
"obj": 0
|
||||
},
|
||||
"jvceqG1K3aGV": {
|
||||
"mat": [
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004
|
||||
],
|
||||
"nums": 2,
|
||||
"obj": 0
|
||||
},
|
||||
"0iMT2yXLK0vj": {
|
||||
"mat": [
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4
|
||||
],
|
||||
"nums": 1,
|
||||
"obj": 0
|
||||
},
|
||||
"wDZ7DtjZN3xx": {
|
||||
"mat": [
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4
|
||||
],
|
||||
"nums": 1,
|
||||
"obj": 0
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@
|
|||
},
|
||||
"scripts": {
|
||||
"start": "NODE_ENV=production; node src/start.js",
|
||||
"server": "nodemon src/start.js --ignore dev/ --ignore public/ --ignore src/client/",
|
||||
"server": "nodemon src/start.js --ignore dev/ --ignore public/ --ignore src/client/ --ignore dist/client/",
|
||||
"rollup:dev": "NODE_ENV=development; rollup -c -w",
|
||||
"rollup:build": "NODE_ENV=production; rollup -c",
|
||||
"test": "node test/test-server.js"
|
||||
|
|
3
src/R.js
3
src/R.js
|
@ -1,8 +1,11 @@
|
|||
import path from 'path';
|
||||
import {fileURLToPath} from 'url';
|
||||
|
||||
export const __dirname = fileURLToPath(import.meta.url);
|
||||
export const dirname = meta => fileURLToPath(import.meta.url);
|
||||
|
||||
export const PROJECT_ROOT = process.cwd();
|
||||
|
||||
export const TAGS = [
|
||||
null,
|
||||
'vehicle',
|
||||
|
|
|
@ -1,105 +1,77 @@
|
|||
import reloadSession from './helpers/reloadSession.js';
|
||||
import getImage from './helpers/getImage.js';
|
||||
import verifyImage from './helpers/verifyImage.js';
|
||||
|
||||
import {createElement as ce, querySelector as qs} from './util/document.js';
|
||||
|
||||
import styles from './res/styles.js';
|
||||
|
||||
|
||||
/**
|
||||
* Create a uCaptcha box
|
||||
* @param {string} websiteKey Website key
|
||||
* @param {Function} [onVerified] Function that will be called on token received
|
||||
* @return {HTMLElement} uCaptcha box
|
||||
*/
|
||||
function uCaptchaBox(websiteKey) {
|
||||
const styles = `
|
||||
#ucaptcha-container {
|
||||
font-family: Arial;
|
||||
}
|
||||
#ucaptcha-next {
|
||||
border: none;
|
||||
background-color: royalblue;
|
||||
color: #FFF;
|
||||
padding: 10px 20px;
|
||||
text-transform: uppercase;
|
||||
border-radius: 3px;
|
||||
}
|
||||
#ucaptcha-grid {
|
||||
width: 384px;
|
||||
height: 384px;
|
||||
transition: .2s ease;
|
||||
}
|
||||
#ucaptcha-grid.blurred {
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
#ucaptcha-grid td {
|
||||
transition: .1s ease;
|
||||
cursor: pointer;
|
||||
padding: 10px;
|
||||
}
|
||||
function uCaptchaBox(websiteKey, onVerified) {
|
||||
/** @type {import('../models/Session.js').Session} */
|
||||
let session;
|
||||
|
||||
#ucaptcha-grid td.selected {
|
||||
padding: 0;
|
||||
border: 10px solid white;
|
||||
}
|
||||
`;
|
||||
const styleTag = ce('style');
|
||||
styleTag.innerHTML = styles;
|
||||
document.head.appendChild(styleTag);
|
||||
|
||||
const checkbox = ce('div', {
|
||||
style: 'height:25px;display:inline-block',
|
||||
});
|
||||
checkbox.style.cursor = 'pointer';
|
||||
checkbox.style.borderRadius = '3px';
|
||||
checkbox.style.border = '2px solid #888';
|
||||
checkbox.style.width = '25px';
|
||||
const captchaBox = ce('div', {id: 'ucaptcha'});
|
||||
|
||||
const captchaBox = ce('div');
|
||||
const checkbox = ce('div', {id: 'uc-checkbox'});
|
||||
captchaBox.appendChild(checkbox);
|
||||
|
||||
const captchaContainer = ce('div', {id: 'ucaptcha-container'});
|
||||
const captchaContainer = ce('div', {id: 'uc-container', style: 'visibility: hidden'});
|
||||
|
||||
const imageTagTitle = ce('h3', {id: 'ucaptcha-caption'});
|
||||
const imageTagTitle = ce('p', {id: 'uc-caption'});
|
||||
captchaContainer.appendChild(imageTagTitle);
|
||||
|
||||
const image = ce('img', {id: 'ucaptcha-img', style: 'position:absolute;display:block;z-index:-999'});
|
||||
const image = ce('img', {id: 'uc-img', style: 'position:absolute;display:block;z-index:-999'});
|
||||
captchaContainer.appendChild(image);
|
||||
|
||||
const btn = ce('button', {id: 'ucaptcha-next'});
|
||||
btn.textContent = 'Next';
|
||||
const imageGrid = ce('table', {id: 'uc-grid'});
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const tr = ce('tr');
|
||||
for (let ii = 0; ii < 4; ii++) {
|
||||
const td = ce('td',
|
||||
{'style': 'cursor:pointer'});
|
||||
td.addEventListener('click', (e)=>{
|
||||
e.target.classList.toggle('selected');
|
||||
});
|
||||
tr.appendChild(td);
|
||||
}
|
||||
imageGrid.appendChild(tr);
|
||||
}
|
||||
captchaContainer.appendChild(imageGrid);
|
||||
|
||||
captchaBox.appendChild(captchaContainer);
|
||||
captchaBox.appendChild(btn);
|
||||
const nextBtn = ce('button', {id: 'uc-next'});
|
||||
nextBtn.textContent = 'Next';
|
||||
nextBtn.addEventListener('click', async (e)=>{
|
||||
await verifyImage(session, captchaBox, captchaContainer, onVerified);
|
||||
});
|
||||
captchaContainer.appendChild(nextBtn);
|
||||
|
||||
checkbox.onclick = async function() {
|
||||
/** @type {import('../models/Session.js').Session} */
|
||||
const session = await reloadSession(websiteKey);
|
||||
const overlay = ce('div', {id: 'uc-overlay', style: 'display: none'});
|
||||
document.body.appendChild(overlay);
|
||||
|
||||
qs('#ucaptcha-caption').innerHTML = `Select all squares with <b>${session['imageTag']}s</b>`;
|
||||
document.body.appendChild(captchaContainer);
|
||||
|
||||
const imageGrid = ce('table', {id: 'ucaptcha-grid'});
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const tr = ce('tr');
|
||||
for (let ii = 0; ii < 4; ii++) {
|
||||
const td = ce('td',
|
||||
{'style': 'cursor:pointer'});
|
||||
td.addEventListener('click', (e)=>{
|
||||
e.target.classList.toggle('selected');
|
||||
});
|
||||
tr.appendChild(td);
|
||||
}
|
||||
imageGrid.appendChild(tr);
|
||||
captchaBox.addEventListener('click', async ()=>{
|
||||
if (!session) {
|
||||
session = await reloadSession(websiteKey);
|
||||
await getImage(session, captchaContainer);
|
||||
qs('#uc-caption').innerHTML = `Select all squares with <b>${session['imageTag']}s</b>`;
|
||||
}
|
||||
|
||||
btn.addEventListener('click', (e)=>{
|
||||
verifyImage(session, captchaBox);
|
||||
});
|
||||
overlay.style.display = 'block';
|
||||
captchaContainer.style.visibility = 'visible';
|
||||
});
|
||||
|
||||
captchaContainer.appendChild(imageGrid);
|
||||
|
||||
getImage(session, captchaBox);
|
||||
|
||||
checkbox.setAttribute('style',
|
||||
checkbox.getAttribute('style') + 'background-color:royalblue;');
|
||||
};
|
||||
return captchaBox;
|
||||
}
|
||||
|
||||
|
@ -107,10 +79,11 @@ function uCaptchaBox(websiteKey) {
|
|||
* Instantiate a uCaptcha box
|
||||
* @param {string} websiteKey
|
||||
* @param {string} selector
|
||||
* @param {Function} [onVerified]
|
||||
*/
|
||||
export function create(websiteKey, selector) {
|
||||
export function create(websiteKey, selector, onVerified) {
|
||||
// const iframe = ce("iframe");
|
||||
// iframe.setAttribute("src", "https://localhost:444/?k="+websiteKey)
|
||||
|
||||
qs(selector).appendChild(uCaptchaBox(websiteKey));
|
||||
qs(selector).appendChild(uCaptchaBox(websiteKey, onVerified));
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import request from '../util/request.js';
|
||||
import {querySelector as qs} from '../util/document.js';
|
||||
|
||||
/**
|
||||
* @typedef {import('../../models/Session.js').Session} Session
|
||||
|
@ -7,16 +8,15 @@ import request from '../util/request.js';
|
|||
/**
|
||||
* Instantiate the uCaptcha loop
|
||||
* @param {Session} session
|
||||
* @param {HTMLElement} captchaBox
|
||||
* @param {HTMLElement} captchaContainer
|
||||
*/
|
||||
export default function getImage(session, captchaBox) {
|
||||
request(`/api/image?s=${session['sessionId']}`, {_responseType: 'blob'})
|
||||
.then((blob)=>{
|
||||
const imageUrl = URL.createObjectURL(blob);
|
||||
console.log(imageUrl);
|
||||
captchaBox
|
||||
.querySelector('#ucaptcha-container')
|
||||
.querySelector('#ucaptcha-img')
|
||||
.setAttribute('src', imageUrl);
|
||||
});
|
||||
export default async function getImage(session, captchaContainer) {
|
||||
const blob = await request(
|
||||
`/api/image?s=${session['sessionId']}`, {_responseType: 'blob'});
|
||||
const imageUrl = URL.createObjectURL(blob);
|
||||
|
||||
captchaContainer.querySelector('#uc-img')
|
||||
.setAttribute('src', imageUrl);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import request from '../util/request.js';
|
||||
import getImage from './getImage.js';
|
||||
import {querySelector as qs} from '../util/document.js';
|
||||
import checkmark from '../res/checkmark.js';
|
||||
|
||||
|
||||
/**
|
||||
|
@ -11,9 +12,12 @@ import {querySelector as qs} from '../util/document.js';
|
|||
* Verify the image
|
||||
* @param {Session} session
|
||||
* @param {HTMLElement} captchaBox
|
||||
* @param {HTMLElement} captchaContainer
|
||||
* @param {Function} [onVerified]
|
||||
*/
|
||||
export default async function verifyImage(session, captchaBox) {
|
||||
const captchaGrid = captchaBox.querySelector('#ucaptcha-grid');
|
||||
export default async function verifyImage(
|
||||
session, captchaBox, captchaContainer, onVerified) {
|
||||
const captchaGrid = captchaContainer.querySelector('#uc-grid');
|
||||
|
||||
captchaGrid.classList.add('blurred');
|
||||
const tds = captchaGrid.querySelectorAll('td');
|
||||
|
@ -37,10 +41,19 @@ export default async function verifyImage(session, captchaBox) {
|
|||
/** @type {Session} */
|
||||
const resp = await request('/api/verify', {_method: 'POST', _body: body});
|
||||
|
||||
qs('#ucaptcha-caption')
|
||||
if (resp.token) {
|
||||
// FIXME: Doesn't work
|
||||
captchaBox.removeEventListener('click', async ()=>{});
|
||||
captchaBox.querySelector('#uc-checkbox').innerHTML += checkmark;
|
||||
captchaContainer.style.visibility = 'hidden';
|
||||
onVerified(resp.token);
|
||||
return;
|
||||
}
|
||||
|
||||
qs('#uc-caption')
|
||||
.innerHTML = `Select all squares with <b>${resp['imageTag']}s</b>`;
|
||||
|
||||
getImage(session, captchaBox);
|
||||
await getImage(session, captchaContainer);
|
||||
|
||||
captchaGrid.classList.remove('blurred');
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
/* eslint-disable max-len */
|
||||
export default
|
||||
`
|
||||
<svg version="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" enable-background="new 0 0 48 48">
|
||||
<polygon fill="#43A047" stroke="#43A047" stroke-width="2" points="40.6,12.1 17,35.7 7.4,26.1 4.6,29 17,41.3 43.4,14.9"/>
|
||||
</svg>
|
||||
|
||||
`;
|
|
@ -0,0 +1,78 @@
|
|||
// For syntax highlighting within VSCode
|
||||
const styled = {
|
||||
div(p) {
|
||||
return p;
|
||||
},
|
||||
};
|
||||
|
||||
export default styled.div`
|
||||
#ucaptcha {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 24px;
|
||||
font-family: Arial;
|
||||
border: 1px solid #AAA;
|
||||
box-shadow: 0 0 4px 1px rgba(0,0,0,0.08);
|
||||
background: #F8F8F8;
|
||||
width: 250px;
|
||||
height: 70px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
#uc-checkbox {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
border: 2px solid #BBB;
|
||||
background: #FFF;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
#ucaptcha:active #uc-checkbox {
|
||||
background: #EEE;
|
||||
}
|
||||
#ucaptcha:hover #uc-checkbox {
|
||||
background: #FCFCFC;
|
||||
}
|
||||
#uc-container {
|
||||
top: 0;
|
||||
position: fixed;
|
||||
z-index: 999999;
|
||||
background: #FFF;
|
||||
padding: 12px;
|
||||
transition: 1s ease all;
|
||||
}
|
||||
#uc-overlay {
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
background: rgba(0,0,0,0.4);
|
||||
z-index: 99999;
|
||||
}
|
||||
#uc-next {
|
||||
border: none;
|
||||
background-color: royalblue;
|
||||
color: #FFF;
|
||||
padding: 10px 20px;
|
||||
text-transform: uppercase;
|
||||
border-radius: 3px;
|
||||
}
|
||||
#uc-grid {
|
||||
width: 384px;
|
||||
height: 384px;
|
||||
transition: .2s ease;
|
||||
}
|
||||
#uc-grid.blurred {
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
#uc-grid td {
|
||||
transition: .1s ease;
|
||||
cursor: pointer;
|
||||
padding: 10px;
|
||||
}
|
||||
#uc-grid td.selected {
|
||||
padding: 0;
|
||||
border: 10px solid white;
|
||||
}
|
||||
`;
|
|
@ -1,4 +1,5 @@
|
|||
import fs from 'fs';
|
||||
import {randomBytes} from './utils.js';
|
||||
import path from 'path';
|
||||
import {client} from '../idb.js';
|
||||
import {PROJECT_ROOT, MAX_SESSION_TIME, TAGS} from '../R.js';
|
||||
|
@ -6,7 +7,9 @@ import {argmaxThresh} from './utils.js';
|
|||
import pickRandomFile from './pickRandomFile.js';
|
||||
|
||||
// Anything above this value is considered a true selection within image matrix
|
||||
const TOLERANCE = 0.6;
|
||||
const MAT_TOLERANCE = process.env.NODE_ENV === 'development' ? 999.0 : 0.6;
|
||||
// A user with a score above this value is considered a human
|
||||
const SCORE_TOLERANCE = 0.9;
|
||||
|
||||
/**
|
||||
* @typedef {Object} FileMat
|
||||
|
@ -95,8 +98,15 @@ export default async function verifyImage(sessionId, userMat) {
|
|||
console.log('Result: ', result);
|
||||
|
||||
let {image, score, websiteKey} = result;
|
||||
|
||||
/** @type {import('../models/Session.js').Session)} */
|
||||
const session = {
|
||||
sessionId,
|
||||
websiteKey,
|
||||
};
|
||||
|
||||
const imageMat = getMat(image);
|
||||
const trueArgmax = argmaxThresh(imageMat.mat, TOLERANCE).join(',');
|
||||
const trueArgmax = argmaxThresh(imageMat.mat, MAT_TOLERANCE).join(',');
|
||||
const userArgmax = argmaxThresh(userMat, 1).join(',');
|
||||
|
||||
updateMat(imageMat, userMat, image);
|
||||
|
@ -115,6 +125,25 @@ export default async function verifyImage(sessionId, userMat) {
|
|||
score += delta;
|
||||
}
|
||||
|
||||
if (score >= SCORE_TOLERANCE) {
|
||||
const nonce = randomBytes(24);
|
||||
session.token = nonce;
|
||||
|
||||
const payload = JSON.stringify({
|
||||
websiteKey,
|
||||
});
|
||||
|
||||
await client.del(sessionId);
|
||||
await client.setex(nonce, 120, payload);
|
||||
return [
|
||||
null,
|
||||
session,
|
||||
{
|
||||
removeCookie: websiteKey,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const newImageFilename = await pickRandomFile();
|
||||
console.log('New image', newImageFilename);
|
||||
// TODO: Make imageTagId dynamic
|
||||
|
@ -129,12 +158,7 @@ export default async function verifyImage(sessionId, userMat) {
|
|||
|
||||
await client.setex(sessionId, MAX_SESSION_TIME, update);
|
||||
|
||||
/** @type {import('../models/Session.js').Session)} */
|
||||
const session = {
|
||||
sessionId,
|
||||
websiteKey,
|
||||
imageTag: TAGS[newImageTagId],
|
||||
};
|
||||
session.imageTag = TAGS[newImageTagId];
|
||||
|
||||
return [
|
||||
null,
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import {client} from '../idb.js';
|
||||
|
||||
/**
|
||||
* Verify token from ucaptcha response
|
||||
* @param {string} secret
|
||||
* @param {string} token
|
||||
*/
|
||||
export default async function verifyToken(secret, token) {
|
||||
// TODO: We need authentication of the secret key!
|
||||
|
||||
const response = {
|
||||
success: null,
|
||||
key: null,
|
||||
errors: [],
|
||||
};
|
||||
|
||||
if (!secret || !token) {
|
||||
response.errors.push('INVALID_REQUEST');
|
||||
return response;
|
||||
}
|
||||
|
||||
const resp = await client.get(token);
|
||||
if (!resp) {
|
||||
response.success = false;
|
||||
return response;
|
||||
}
|
||||
|
||||
await client.del(token);
|
||||
|
||||
const result = JSON.parse(resp);
|
||||
|
||||
response.success = true;
|
||||
response.key = result.websiteKey;
|
||||
return response;
|
||||
}
|
|
@ -3,13 +3,13 @@
|
|||
* @property {string} sessionId User session ID
|
||||
* @property {string} websiteKey Website ID
|
||||
* @property {string} [imageTag] Human readable text image tag
|
||||
* @property {string} [token] Token used for verification of session
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} IDBSession User session model in IDB
|
||||
* @property {string} sessionId User session ID
|
||||
* @property {string} websiteKey Website ID
|
||||
* @property {string} imageTag Human readable text image tag
|
||||
* @property {string} image Image URL for challenge
|
||||
* @property {number} obj Image object ID
|
||||
* @property {number} score How likely is the user to be a human?
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
import express from 'express';
|
||||
import verifyToken from '../../helpers/verifyToken.js';
|
||||
|
||||
const router = new express.Router();
|
||||
|
||||
/**
|
||||
* @param {express.Request} req
|
||||
* @param {express.Response} res
|
||||
*/
|
||||
async function tokenVerifyHandler(req, res) {
|
||||
const {secret, token} = req.body;
|
||||
|
||||
const result = await verifyToken(secret, token);
|
||||
|
||||
if (result.errors.length) {
|
||||
res.status(400).json(result);
|
||||
return;
|
||||
}
|
||||
|
||||
res.json(result);
|
||||
}
|
||||
|
||||
export default router.post('/tokenverify', tokenVerifyHandler);
|
|
@ -12,11 +12,17 @@ const verifyImageHandler = async (req, res)=>{
|
|||
const sessionId = req.body.s;
|
||||
const mat = req.body.mat;
|
||||
|
||||
const [error, session] = await verifyImage(sessionId, mat);
|
||||
let [error, session, actions] = await verifyImage(sessionId, mat);
|
||||
actions = actions || {};
|
||||
if (error) {
|
||||
sendJson(res, {
|
||||
error,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (actions.removeCookie) {
|
||||
res.clearCookie(actions.removeCookie);
|
||||
}
|
||||
|
||||
sendJson(res, session);
|
||||
|
|
|
@ -23,6 +23,13 @@ if (process.env.NODE_ENV === 'development') {
|
|||
import apiImage from './routes/api/image.js';
|
||||
import apiVerify from './routes/api/verify.js';
|
||||
import apiReload from './routes/api/reload.js';
|
||||
app.use('/api', [apiImage, apiVerify, apiReload]);
|
||||
import apiTokenVerify from './routes/api/tokenverify.js';
|
||||
|
||||
app.use('/api', [
|
||||
apiImage,
|
||||
apiVerify,
|
||||
apiReload,
|
||||
apiTokenVerify,
|
||||
]);
|
||||
|
||||
export {app};
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>uCaptcha Test</title>
|
||||
<script src="../dist/client/bundle.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="captcha-box"></div>
|
||||
|
||||
<script>
|
||||
ucaptcha.create("hUYUhuytryftyi6787yYtg67", ".captcha-box")
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue