Added image tags and update readme
This commit is contained in:
parent
a58928f060
commit
bdae28a825
|
@ -7,7 +7,11 @@ It has much the same API and has an additional benefit of helping [OpenStreetCam
|
|||
|
||||
# Donate
|
||||
If you use uCaptcha in production, or want to see the project grow, consider funding the project to help pay for hosting and bandwidth costs.
|
||||
|
||||
|
||||
XMR: `45uYweaLaasTFKdEHK9qADRZMatRE3U9vKFwT1kpaig26GnruZm1t21ipVjsC1KLeeL7sG3m8bHfhcce4tQJLDKNCFBLPar`
|
||||
|
||||
|
||||
BTC: `bc1qjj45cuu7d8r6g83al7yrtkkmzu6ud4j9qzq3dn`
|
||||
|
||||
|
||||
|
@ -16,6 +20,7 @@ BTC: `bc1qjj45cuu7d8r6g83al7yrtkkmzu6ud4j9qzq3dn`
|
|||
- [ ] Think of a way to detect if user's selection is correct answer
|
||||
- [ ] Create tests
|
||||
- [ ] Configure Rollup to create 2 packages 1x esm 1x cjs
|
||||
- [ ] Improve the UI
|
||||
|
||||
## Medium term (> 1 month)
|
||||
- [ ] Increase security and accuracy of predictions
|
||||
|
|
47
dev/mat.json
47
dev/mat.json
|
@ -1,46 +1 @@
|
|||
{
|
||||
"B4mXt9gEOp1r": {
|
||||
"mat": [
|
||||
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.4,
|
||||
0.6,
|
||||
0.6,
|
||||
0.6
|
||||
],
|
||||
"nums": 1,
|
||||
"obj": 0
|
||||
},
|
||||
"U63lqPRdPinG": {
|
||||
"mat": [
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.30000000000000004,
|
||||
0.7,
|
||||
0.7,
|
||||
0.7,
|
||||
0.30000000000000004,
|
||||
0.7,
|
||||
0.7,
|
||||
0.7
|
||||
],
|
||||
"nums": 2,
|
||||
"obj": 0
|
||||
}
|
||||
}
|
||||
{}
|
6
src/R.js
6
src/R.js
|
@ -11,3 +11,9 @@ export const PROJECT_ROOT = process.cwd();
|
|||
export const IMAGES_FOLDER = path.join(PROJECT_ROOT, 'public', 'images');
|
||||
|
||||
export const MAX_SESSION_TIME = 60 * 30;
|
||||
|
||||
export const TAGS = [
|
||||
null,
|
||||
'vehicle',
|
||||
'house',
|
||||
]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import reloadSession from './helpers/reloadSession.js';
|
||||
import getImage from './helpers/getImage.js';
|
||||
import verifyImage from './helpers/verifyImage.js';
|
||||
import {createElement} from './util/document.js';
|
||||
import {createElement as ce, querySelector as qs} from './util/document.js';
|
||||
|
||||
/**
|
||||
* Create a uCaptcha box
|
||||
|
@ -10,6 +10,9 @@ import {createElement} from './util/document.js';
|
|||
*/
|
||||
function uCaptchaBox(websiteKey) {
|
||||
const styles = `
|
||||
#ucaptcha-container {
|
||||
font-family: Arial;
|
||||
}
|
||||
#ucaptcha-next {
|
||||
border: none;
|
||||
background-color: royalblue;
|
||||
|
@ -21,6 +24,10 @@ function uCaptchaBox(websiteKey) {
|
|||
#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;
|
||||
|
@ -33,11 +40,11 @@ function uCaptchaBox(websiteKey) {
|
|||
border: 10px solid white;
|
||||
}
|
||||
`;
|
||||
const styleTag = createElement('style');
|
||||
const styleTag = ce('style');
|
||||
styleTag.innerHTML = styles;
|
||||
document.head.appendChild(styleTag);
|
||||
|
||||
const checkbox = createElement('div', {
|
||||
const checkbox = ce('div', {
|
||||
style: 'height:25px;display:inline-block',
|
||||
});
|
||||
checkbox.style.cursor = 'pointer';
|
||||
|
@ -45,29 +52,34 @@ function uCaptchaBox(websiteKey) {
|
|||
checkbox.style.border = '2px solid #888';
|
||||
checkbox.style.width = '25px';
|
||||
|
||||
const captchaBox = createElement('div');
|
||||
const captchaBox = ce('div');
|
||||
captchaBox.appendChild(checkbox);
|
||||
|
||||
const imageContainer = createElement('div', {id: 'ucaptcha-container'});
|
||||
const captchaContainer = ce('div', {id: 'ucaptcha-container'});
|
||||
|
||||
const image = createElement('img', {id: 'ucaptcha-img', style: 'position:absolute;display:block;z-index:-999'});
|
||||
imageContainer.appendChild(image);
|
||||
const imageTagTitle = ce('h3', {id: 'ucaptcha-caption'});
|
||||
captchaContainer.appendChild(imageTagTitle);
|
||||
|
||||
const btn = createElement('button', {id: 'ucaptcha-next'});
|
||||
const image = ce('img', {id: 'ucaptcha-img', style: 'position:absolute;display:block;z-index:-999'});
|
||||
captchaContainer.appendChild(image);
|
||||
|
||||
const btn = ce('button', {id: 'ucaptcha-next'});
|
||||
btn.textContent = 'Next';
|
||||
|
||||
captchaBox.appendChild(imageContainer);
|
||||
captchaBox.appendChild(captchaContainer);
|
||||
captchaBox.appendChild(btn);
|
||||
|
||||
checkbox.onclick = async function() {
|
||||
/** @type {import('../shared/models/UserSession.js').UserSession} */
|
||||
const session = await reloadSession(websiteKey);
|
||||
|
||||
const imageGrid = createElement('table', {id: 'ucaptcha-grid'});
|
||||
qs('#ucaptcha-caption').innerHTML = `Select all squares with <b>${session.getImageTag()}s</b>`;
|
||||
|
||||
const imageGrid = ce('table', {id: 'ucaptcha-grid'});
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const tr = createElement('tr');
|
||||
const tr = ce('tr');
|
||||
for (let ii = 0; ii < 4; ii++) {
|
||||
const td = createElement('td',
|
||||
const td = ce('td',
|
||||
{'style': 'cursor:pointer'});
|
||||
td.addEventListener('click', (e)=>{
|
||||
e.target.classList.toggle('selected');
|
||||
|
@ -82,7 +94,7 @@ function uCaptchaBox(websiteKey) {
|
|||
getImage(session, captchaBox);
|
||||
});
|
||||
|
||||
imageContainer.appendChild(imageGrid);
|
||||
captchaContainer.appendChild(imageGrid);
|
||||
|
||||
getImage(session, captchaBox);
|
||||
|
||||
|
@ -98,8 +110,8 @@ function uCaptchaBox(websiteKey) {
|
|||
* @param {string} selector
|
||||
*/
|
||||
export function create(websiteKey, selector) {
|
||||
// const iframe = createElement("iframe");
|
||||
// const iframe = ce("iframe");
|
||||
// iframe.setAttribute("src", "https://localhost:444/?k="+websiteKey)
|
||||
|
||||
document.querySelector(selector).appendChild(uCaptchaBox(websiteKey));
|
||||
qs(selector).appendChild(uCaptchaBox(websiteKey));
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import UserSession from '../../models/UserSession.js';
|
|||
* @param {string} websiteKey
|
||||
* @return {Promise<UserSession>}
|
||||
*/
|
||||
export default function initializeSession(websiteKey) {
|
||||
export default function reloadSession(websiteKey) {
|
||||
return new Promise((resolve, reject)=>{
|
||||
request(`/api/reload?k=${websiteKey}`)
|
||||
.then((resp)=>{
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import request from '../util/request.js';
|
||||
import UserSession from '../../models/UserSession.js';
|
||||
import {querySelector as qs} from '../util/document.js';
|
||||
|
||||
/**
|
||||
* @typedef { import('../../models/UserSession.js').UserSession } UserSession
|
||||
* @typedef {import('../../models/UserSession.js')} UserSession
|
||||
*/
|
||||
|
||||
|
||||
|
@ -10,7 +12,8 @@ import request from '../util/request.js';
|
|||
* @param {UserSession} session
|
||||
* @param {HTMLElement} captchaGrid
|
||||
*/
|
||||
export async function verfiyImage(session, captchaGrid) {
|
||||
export default async function verifyImage(session, captchaGrid) {
|
||||
captchaGrid.classList.add('blurred');
|
||||
const tds = captchaGrid.querySelectorAll('td');
|
||||
const selectedTds = [];
|
||||
|
||||
|
@ -29,11 +32,15 @@ export async function verfiyImage(session, captchaGrid) {
|
|||
'mat': mat,
|
||||
};
|
||||
|
||||
await request('/api/verify', {_method: 'POST', _body: body});
|
||||
const resp = await request('/api/verify', {_method: 'POST', _body: body});
|
||||
|
||||
session.deserialize(resp);
|
||||
qs('#ucaptcha-caption')
|
||||
.innerHTML = `Select all squares with <b>${session.getImageTag()}s</b>`;
|
||||
|
||||
captchaGrid.classList.remove('blurred');
|
||||
|
||||
selectedTds.map((td)=>{
|
||||
td.classList.remove('selected');
|
||||
});
|
||||
}
|
||||
|
||||
export default verfiyImage;
|
||||
|
|
|
@ -7,4 +7,3 @@
|
|||
* @property {number} obj Image object ID
|
||||
* @property {number} score How likely is the user to be a human?
|
||||
*/
|
||||
export const IDBSession = {};
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
export default [
|
||||
null,
|
||||
'Vehicle',
|
||||
'House',
|
||||
];
|
|
@ -8,6 +8,9 @@ export default function UserSession() {
|
|||
/** @private @type {string | undefined} */
|
||||
this._websiteKey = undefined;
|
||||
|
||||
/** @private @type {string | undefined} */
|
||||
this._imageTag = undefined;
|
||||
|
||||
|
||||
/**
|
||||
* Get session identifier
|
||||
|
@ -43,6 +46,23 @@ export default function UserSession() {
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get image tag as text
|
||||
* @return {string}
|
||||
*/
|
||||
this.getImageTag = function() {
|
||||
return this._imageTag;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set image tag as text
|
||||
* @param {string} imageObject
|
||||
*/
|
||||
this.setImageTag = function(imageObject) {
|
||||
this._imageTag = imageObject;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serialize the session into a JSON object
|
||||
* @return {Array<any>}
|
||||
|
@ -50,14 +70,12 @@ export default function UserSession() {
|
|||
this.serialize = function() {
|
||||
const sessionId = this.getSessionId();
|
||||
const websiteKey = this.getWebsiteKey();
|
||||
|
||||
if (!sessionId || !websiteKey) {
|
||||
throw Error();
|
||||
}
|
||||
const imageTag = this.getImageTag();
|
||||
|
||||
return [
|
||||
sessionId,
|
||||
websiteKey,
|
||||
imageTag,
|
||||
];
|
||||
};
|
||||
|
||||
|
@ -65,9 +83,13 @@ export default function UserSession() {
|
|||
* Deserialize session data from JSON object
|
||||
* @param {Array<any>} payload
|
||||
*/
|
||||
this.deserialize = function([_sessionId, _websiteKey]) {
|
||||
console.log(_sessionId, _websiteKey);
|
||||
this.setSessionId(_sessionId);
|
||||
this.setWebsiteKey(_websiteKey);
|
||||
this.deserialize = function([
|
||||
sessionId,
|
||||
websiteKey,
|
||||
imageTag,
|
||||
]) {
|
||||
this.setSessionId(sessionId);
|
||||
this.setWebsiteKey(websiteKey);
|
||||
this.setImageTag(imageTag);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -49,10 +49,8 @@ router.post('/verify', async (req, res)=>{
|
|||
const mat = req.body.mat;
|
||||
console.log(req.body);
|
||||
|
||||
await verifyImage(sessionId, mat);
|
||||
sendJson(res, {
|
||||
ok: 1,
|
||||
});
|
||||
const session = await verifyImage(sessionId, mat);
|
||||
sendJson(res, session.serialize());
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
|
|
@ -5,11 +5,11 @@ import pickRandomFile from './pickRandomFile.js';
|
|||
import UserSession from '../../models/UserSession.js';
|
||||
import {randomBytes} from '../../helpers/utils.js';
|
||||
import {client} from '../../helpers/idb.js';
|
||||
import {MAX_SESSION_TIME} from '../../R.js';
|
||||
import {MAX_SESSION_TIME, TAGS} from '../../R.js';
|
||||
|
||||
/**
|
||||
* @typedef {import('../../models/IDBSession.js').IDBSession} IDBSession
|
||||
*/
|
||||
// /**
|
||||
// * @typedef {import('../../models/IDBSession.js')} IDBSession
|
||||
// */
|
||||
|
||||
|
||||
/**
|
||||
|
@ -20,28 +20,27 @@ import {MAX_SESSION_TIME} from '../../R.js';
|
|||
export default async function initializeSession(websiteKey, cookies) {
|
||||
let randomSessionId;
|
||||
|
||||
const session = new UserSession();
|
||||
if (cookies[websiteKey]) {
|
||||
randomSessionId = cookies[websiteKey];
|
||||
|
||||
session.setSessionId(randomSessionId);
|
||||
session.setWebsiteKey(websiteKey);
|
||||
|
||||
return session;
|
||||
} else {
|
||||
randomSessionId = randomBytes(8);
|
||||
}
|
||||
|
||||
const image = await pickRandomFile();
|
||||
|
||||
const imageTagId = 1; // TODO: Change later to be dynamic based on `image`
|
||||
const session = new UserSession();
|
||||
|
||||
session.setSessionId(randomSessionId);
|
||||
session.setWebsiteKey(websiteKey);
|
||||
session.setImageTag(TAGS[imageTagId]);
|
||||
|
||||
/** @type {IDBSession} */
|
||||
/** @type {import('../../models/IDBSession.js')} */
|
||||
const idbPayload = {
|
||||
sessionId: session.getSessionId(),
|
||||
websiteKey: session.getWebsiteKey(),
|
||||
image,
|
||||
obj: imageTagId,
|
||||
score: 0.5,
|
||||
};
|
||||
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import {client} from '../../helpers/idb.js';
|
||||
import {PROJECT_ROOT, MAX_SESSION_TIME} from '../../R.js';
|
||||
import {PROJECT_ROOT, MAX_SESSION_TIME, TAGS} from '../../R.js';
|
||||
import {argmaxThresh} from '../../helpers/utils.js';
|
||||
import pickRandomFile from './pickRandomFile.js';
|
||||
import UserSession from '../../models/UserSession.js';
|
||||
|
||||
|
||||
/** @typedef {import('../../models/IDBSession.js')} IDBSession */
|
||||
|
||||
/**
|
||||
* @typedef {Object} FileMat
|
||||
|
@ -80,13 +84,12 @@ function updateMat(imageMat, userMat, image) {
|
|||
*/
|
||||
export default function(sessionId, userMat) {
|
||||
return new Promise((resolve, reject)=>{
|
||||
client.get(sessionId, (err, result)=>{
|
||||
console.log(sessionId);
|
||||
client.get(sessionId, (err, resp)=>{
|
||||
if (err) return reject(err);
|
||||
if (!result) return reject(new Error('Session expired'));
|
||||
if (!resp) return reject(new Error('Session expired'));
|
||||
|
||||
/** @type {import('../../models/IDBSession.js').IDBSession} */
|
||||
result = JSON.parse(result);
|
||||
/** @type {IDBSession} */
|
||||
const result = JSON.parse(resp);
|
||||
|
||||
const imageMat = getMat(result.image);
|
||||
const trueArgmax = argmaxThresh(imageMat.mat, 0.6).join(',');
|
||||
|
@ -109,13 +112,23 @@ export default function(sessionId, userMat) {
|
|||
}
|
||||
|
||||
pickRandomFile().then((image)=>{
|
||||
// TODO: Make me dynamic
|
||||
const newImageTag = 1;
|
||||
const update = JSON.stringify(
|
||||
Object.assign(result, {image: image}));
|
||||
Object.assign(result, {
|
||||
image: image,
|
||||
obj: newImageTag,
|
||||
}));
|
||||
|
||||
client.setex(sessionId, MAX_SESSION_TIME,
|
||||
update, (err)=>{
|
||||
if (err) return reject(err);
|
||||
resolve();
|
||||
const session = new UserSession();
|
||||
session.setSessionId(sessionId);
|
||||
session.setWebsiteKey(result.websiteKey);
|
||||
session.setImageTag(TAGS[newImageTag]);
|
||||
|
||||
resolve(session);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue