mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 11:29:46 +00:00
Add captcha verify controller
This commit is contained in:
parent
e57dd8911c
commit
467a49bd40
2 changed files with 57 additions and 1 deletions
|
|
@ -38,7 +38,7 @@ import {
|
|||
import { appCredentialsController, createAppController } from '@/controllers/api/apps.ts';
|
||||
import { blocksController } from '@/controllers/api/blocks.ts';
|
||||
import { bookmarksController } from '@/controllers/api/bookmarks.ts';
|
||||
import { captchaController } from '@/controllers/api/captcha.ts';
|
||||
import { captchaController, captchaVerifyController } from '@/controllers/api/captcha.ts';
|
||||
import {
|
||||
adminRelaysController,
|
||||
adminSetRelaysController,
|
||||
|
|
@ -280,6 +280,7 @@ app.post('/api/v1/ditto/names', requireSigner, nameRequestController);
|
|||
app.get('/api/v1/ditto/names', requireSigner, nameRequestsController);
|
||||
|
||||
app.get('/api/v1/ditto/captcha', captchaController);
|
||||
app.post('/api/v1/ditto/captcha/:id/verify', requireSigner, captchaVerifyController);
|
||||
|
||||
app.get('/api/v1/ditto/zap_splits', getZapSplitsController);
|
||||
app.get('/api/v1/ditto/:id{[0-9a-f]{64}}/zap_splits', statusZapSplitsController);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { createCanvas, loadImage } from '@gfx/canvas-wasm';
|
||||
import TTLCache from '@isaacs/ttlcache';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { AppController } from '@/app.ts';
|
||||
import { Conf } from '@/config.ts';
|
||||
|
|
@ -9,6 +10,11 @@ interface Point {
|
|||
y: number;
|
||||
}
|
||||
|
||||
interface Dimensions {
|
||||
w: number;
|
||||
h: number;
|
||||
}
|
||||
|
||||
const captchas = new TTLCache<string, Point>();
|
||||
|
||||
/** Puzzle captcha controller. */
|
||||
|
|
@ -103,3 +109,52 @@ function getPieceCoords(cw: number, ch: number, pw: number, ph: number): Point {
|
|||
|
||||
return { x, y };
|
||||
}
|
||||
|
||||
const pointSchema = z.object({
|
||||
x: z.number(),
|
||||
y: z.number(),
|
||||
});
|
||||
|
||||
/** Verify the captcha solution and sign an event in the database. */
|
||||
export const captchaVerifyController: AppController = async (c) => {
|
||||
const id = c.req.param('id');
|
||||
const result = pointSchema.safeParse(await c.req.json());
|
||||
|
||||
if (!result.success) {
|
||||
return c.json({ error: 'Invalid input' }, { status: 422 });
|
||||
}
|
||||
|
||||
const solution = captchas.get(id);
|
||||
|
||||
if (!solution) {
|
||||
return c.json({ error: 'Captcha expired' }, { status: 410 });
|
||||
}
|
||||
|
||||
const dim = { w: 50, h: 50 };
|
||||
const point = result.data;
|
||||
|
||||
const success = areIntersecting(
|
||||
{ ...point, ...dim },
|
||||
{ ...solution, ...dim },
|
||||
);
|
||||
|
||||
if (success) {
|
||||
captchas.delete(id);
|
||||
return new Response(null, { status: 204 });
|
||||
}
|
||||
|
||||
return c.json({ error: 'Incorrect solution' }, { status: 400 });
|
||||
};
|
||||
|
||||
type Rectangle = Point & Dimensions;
|
||||
|
||||
function areIntersecting(rect1: Rectangle, rect2: Rectangle, threshold = 0.5) {
|
||||
const r1cx = rect1.x + rect1.w / 2;
|
||||
const r2cx = rect2.x + rect2.w / 2;
|
||||
const r1cy = rect1.y + rect1.h / 2;
|
||||
const r2cy = rect2.y + rect2.h / 2;
|
||||
const dist = Math.sqrt((r2cx - r1cx) ** 2 + (r2cy - r1cy) ** 2);
|
||||
const e1 = Math.sqrt(rect1.h ** 2 + rect1.w ** 2) / 2;
|
||||
const e2 = Math.sqrt(rect2.h ** 2 + rect2.w ** 2) / 2;
|
||||
return dist < (e1 + e2) * threshold;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue