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 { appCredentialsController, createAppController } from '@/controllers/api/apps.ts';
|
||||||
import { blocksController } from '@/controllers/api/blocks.ts';
|
import { blocksController } from '@/controllers/api/blocks.ts';
|
||||||
import { bookmarksController } from '@/controllers/api/bookmarks.ts';
|
import { bookmarksController } from '@/controllers/api/bookmarks.ts';
|
||||||
import { captchaController } from '@/controllers/api/captcha.ts';
|
import { captchaController, captchaVerifyController } from '@/controllers/api/captcha.ts';
|
||||||
import {
|
import {
|
||||||
adminRelaysController,
|
adminRelaysController,
|
||||||
adminSetRelaysController,
|
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/names', requireSigner, nameRequestsController);
|
||||||
|
|
||||||
app.get('/api/v1/ditto/captcha', captchaController);
|
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/zap_splits', getZapSplitsController);
|
||||||
app.get('/api/v1/ditto/:id{[0-9a-f]{64}}/zap_splits', statusZapSplitsController);
|
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 { createCanvas, loadImage } from '@gfx/canvas-wasm';
|
||||||
import TTLCache from '@isaacs/ttlcache';
|
import TTLCache from '@isaacs/ttlcache';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { AppController } from '@/app.ts';
|
import { AppController } from '@/app.ts';
|
||||||
import { Conf } from '@/config.ts';
|
import { Conf } from '@/config.ts';
|
||||||
|
|
@ -9,6 +10,11 @@ interface Point {
|
||||||
y: number;
|
y: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Dimensions {
|
||||||
|
w: number;
|
||||||
|
h: number;
|
||||||
|
}
|
||||||
|
|
||||||
const captchas = new TTLCache<string, Point>();
|
const captchas = new TTLCache<string, Point>();
|
||||||
|
|
||||||
/** Puzzle captcha controller. */
|
/** Puzzle captcha controller. */
|
||||||
|
|
@ -103,3 +109,52 @@ function getPieceCoords(cw: number, ch: number, pw: number, ph: number): Point {
|
||||||
|
|
||||||
return { x, y };
|
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