add captcha
This commit is contained in:
parent
a51f3fff3c
commit
a663ffc490
12 changed files with 115 additions and 7 deletions
52
client/captcha.js
Normal file
52
client/captcha.js
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
/* https://raw.githubusercontent.com/napa3um/node-captcha/refs/heads/master/captcha.js */
|
||||||
|
|
||||||
|
import Route from "../route.js";
|
||||||
|
import initDb from "../db.js";
|
||||||
|
import Canvas from "canvas";
|
||||||
|
import crypto from "node:crypto";
|
||||||
|
|
||||||
|
let db = await initDb();
|
||||||
|
|
||||||
|
// TODO: rewrite
|
||||||
|
let main = new Route([], async function (req, res, input) {
|
||||||
|
const canvas = new Canvas.Canvas(250, 100)
|
||||||
|
const ctx = canvas.getContext('2d')
|
||||||
|
|
||||||
|
ctx.antialias = 'gray'
|
||||||
|
ctx.fillStyle = 'black'
|
||||||
|
ctx.fillRect(0, 0, 250, 100)
|
||||||
|
ctx.fillStyle = 'red'
|
||||||
|
ctx.lineWidth = 3
|
||||||
|
ctx.strokeStyle = 'red'
|
||||||
|
ctx.font = `40px sans`
|
||||||
|
|
||||||
|
// draw two curve lines:
|
||||||
|
for (var i = 0; i < 2; i++) {
|
||||||
|
ctx.moveTo(Math.floor(0.08 * 250), Math.random() * 100)
|
||||||
|
ctx.bezierCurveTo(Math.floor(0.32 * 250), Math.random() * 100, Math.floor(1.07 * 100), Math.random() * 100, Math.floor(0.92 * 250), Math.random() * 100)
|
||||||
|
ctx.stroke()
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw text:
|
||||||
|
const text = ('' + crypto.randomBytes(4).readUIntBE(0, 4)).substring(2, 10)
|
||||||
|
text.split('').forEach((char, i) => {
|
||||||
|
ctx.setTransform(Math.random() * 0.5 + 1, Math.random() * 0.4, Math.random() * 0.4, Math.random() * 0.5 + 1, Math.floor(0.375 * 50) * i + Math.floor(0.25 * 10), Math.floor(1.25 * 40))
|
||||||
|
ctx.fillText(char, 0, 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
let ck = crypto.randomUUID();
|
||||||
|
|
||||||
|
res.cookie('captcha', ck);
|
||||||
|
await db.run('INSERT INTO captcha (key, solution) VALUES (?,?)', [
|
||||||
|
ck,
|
||||||
|
text
|
||||||
|
])
|
||||||
|
|
||||||
|
// send image:
|
||||||
|
res.type('jpg')
|
||||||
|
res.header('Cache-Control', 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0')
|
||||||
|
res.header('Expires', 'Sun, 19 May 1984 02:00:00 GMT')
|
||||||
|
canvas.jpegStream().pipe(res)
|
||||||
|
});
|
||||||
|
|
||||||
|
export default main;
|
1
db.js
1
db.js
|
@ -20,6 +20,7 @@ async function initDb() {
|
||||||
await db.run('CREATE TABLE IF NOT EXISTS follow (username TEXT, target TEXT);');
|
await db.run('CREATE TABLE IF NOT EXISTS follow (username TEXT, target TEXT);');
|
||||||
|
|
||||||
await db.run(`CREATE TABLE IF NOT EXISTS user (username TEXT, bio TEXT);`);
|
await db.run(`CREATE TABLE IF NOT EXISTS user (username TEXT, bio TEXT);`);
|
||||||
|
await db.run(`CREATE TABLE IF NOT EXISTS captcha (key TEXT, solution TEXT);`);
|
||||||
|
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|
26
form/captcha.js
Normal file
26
form/captcha.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import Route from "../route.js";
|
||||||
|
import initDb from "../db.js";
|
||||||
|
|
||||||
|
let db = await initDb();
|
||||||
|
|
||||||
|
let main = new Route([], async function (req, res, input) {
|
||||||
|
let body = { ...req.cookies, ...req.body };
|
||||||
|
|
||||||
|
let { captcha, challenger } = body;
|
||||||
|
|
||||||
|
let match = await db.all('SELECT * FROM captcha WHERE key = ? AND solution = ?', [
|
||||||
|
captcha,
|
||||||
|
challenger
|
||||||
|
]);
|
||||||
|
|
||||||
|
await db.all('DELETE FROM captcha WHERE key = ? AND solution = ?', [
|
||||||
|
captcha,
|
||||||
|
challenger
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
captchaMatch: (match.length > 0)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
export default main;
|
|
@ -2,11 +2,16 @@ import Route from "../route.js";
|
||||||
import initDb from "../db.js";
|
import initDb from "../db.js";
|
||||||
import { randomUUID } from 'node:crypto';
|
import { randomUUID } from 'node:crypto';
|
||||||
import auth from "../form/auth.js";
|
import auth from "../form/auth.js";
|
||||||
|
import captcha from "./captcha.js";
|
||||||
|
|
||||||
let db = await initDb();
|
let db = await initDb();
|
||||||
|
|
||||||
// TODO: rewrite
|
// TODO: rewrite
|
||||||
let main = new Route([auth], async function (req, res, input) {
|
let main = new Route([auth,captcha], async function (req, res, input) {
|
||||||
|
let { captchaMatch} = input;
|
||||||
|
|
||||||
|
if (!captchaMatch) return { 'success': false, 'message': 'Captcha is incorrect' };
|
||||||
|
|
||||||
let { username } = input;
|
let { username } = input;
|
||||||
let id = randomUUID();
|
let id = randomUUID();
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,16 @@ import Route from "../route.js";
|
||||||
import initDb from "../db.js";
|
import initDb from "../db.js";
|
||||||
import { compare } from "bcrypt";
|
import { compare } from "bcrypt";
|
||||||
import { randomBytes } from 'node:crypto';
|
import { randomBytes } from 'node:crypto';
|
||||||
|
import captcha from "./captcha.js";
|
||||||
|
|
||||||
let db = await initDb();
|
let db = await initDb();
|
||||||
|
|
||||||
// TODO: rewrite
|
// TODO: rewrite
|
||||||
let main = new Route([], async function (req, res, input) {
|
let main = new Route([captcha], async function (req, res, input) {
|
||||||
let { user, pass } = req.body;
|
let { user, pass } = req.body;
|
||||||
|
let { captchaMatch} = input;
|
||||||
|
|
||||||
|
if (!captchaMatch) return { 'success': false, 'message': 'Captcha is incorrect' };
|
||||||
|
|
||||||
if (!pass || !user) return { 'success': false, 'message': 'Some fields are missing' };
|
if (!pass || !user) return { 'success': false, 'message': 'Some fields are missing' };
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import Route from "../route.js";
|
import Route from "../route.js";
|
||||||
import initDb from "../db.js";
|
import initDb from "../db.js";
|
||||||
import { hash } from "bcrypt";
|
import { hash } from "bcrypt";
|
||||||
|
import captcha from "./captcha.js";
|
||||||
|
|
||||||
const minChar = 1;
|
const minChar = 1;
|
||||||
const maxChar = 32;
|
const maxChar = 32;
|
||||||
|
@ -13,8 +14,11 @@ function isValid(user) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: rewrite
|
// TODO: rewrite
|
||||||
let main = new Route([], async function (req, res, input) {
|
let main = new Route([captcha], async function (req, res, input) {
|
||||||
let { user, pass, pass2 } = req.body;
|
let { user, pass, pass2 } = req.body;
|
||||||
|
let { captchaMatch} = input;
|
||||||
|
|
||||||
|
if (!captchaMatch) return { 'success': false, 'message': 'Captcha is incorrect' };
|
||||||
|
|
||||||
if (pass != pass2) return { 'success': false, 'message': 'Passwords do not match' };
|
if (pass != pass2) return { 'success': false, 'message': 'Passwords do not match' };
|
||||||
|
|
||||||
|
|
|
@ -3,13 +3,17 @@ import initDb from "../db.js";
|
||||||
import auth from "../form/auth.js";
|
import auth from "../form/auth.js";
|
||||||
import {exec} from 'node:child_process';
|
import {exec} from 'node:child_process';
|
||||||
import { promisify } from "node:util";
|
import { promisify } from "node:util";
|
||||||
|
import captcha from "./captcha.js";
|
||||||
|
|
||||||
const execP = promisify(exec);
|
const execP = promisify(exec);
|
||||||
|
|
||||||
let db = await initDb();
|
let db = await initDb();
|
||||||
|
|
||||||
// TODO: rewrite
|
// TODO: rewrite
|
||||||
let main = new Route([auth], async function (req, res, input) {
|
let main = new Route([auth,captcha], async function (req, res, input) {
|
||||||
|
let { captchaMatch} = input;
|
||||||
|
|
||||||
|
if (!captchaMatch) return { 'success': false, 'message': 'Captcha is incorrect' };
|
||||||
let { path } = req.file;
|
let { path } = req.file;
|
||||||
|
|
||||||
let { username } = input;
|
let { username } = input;
|
||||||
|
|
|
@ -4,13 +4,17 @@ import { randomUUID } from 'node:crypto';
|
||||||
import {exec} from 'node:child_process';
|
import {exec} from 'node:child_process';
|
||||||
import { promisify } from "node:util";
|
import { promisify } from "node:util";
|
||||||
import auth from "../form/auth.js";
|
import auth from "../form/auth.js";
|
||||||
|
import captcha from "./captcha.js";
|
||||||
|
|
||||||
const execP = promisify(exec);
|
const execP = promisify(exec);
|
||||||
|
|
||||||
let db = await initDb();
|
let db = await initDb();
|
||||||
|
|
||||||
// TODO: rewrite
|
// TODO: rewrite
|
||||||
let main = new Route([auth], async function (req, res, input) {
|
let main = new Route([auth,captcha], async function (req, res, input) {
|
||||||
|
let { captchaMatch} = input;
|
||||||
|
|
||||||
|
if (!captchaMatch) return { 'success': false, 'message': 'Captcha is incorrect' };
|
||||||
let { username } = input;
|
let { username } = input;
|
||||||
let id = randomUUID();
|
let id = randomUUID();
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bcrypt": "^5.1.1",
|
"bcrypt": "^5.1.1",
|
||||||
|
"canvas": "^2.11.2",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.6",
|
||||||
"ejs": "^3.1.10",
|
"ejs": "^3.1.10",
|
||||||
"express": "^4.19.2",
|
"express": "^4.19.2",
|
||||||
|
|
|
@ -7,6 +7,7 @@ import user from "./client/user.js";
|
||||||
import settings from "./client/settings.js";
|
import settings from "./client/settings.js";
|
||||||
import e404 from "./client/e404.js";
|
import e404 from "./client/e404.js";
|
||||||
import tou from "./client/tou.js";
|
import tou from "./client/tou.js";
|
||||||
|
import captcha from './client/captcha.js';
|
||||||
|
|
||||||
import loginB from "./form/login.js";
|
import loginB from "./form/login.js";
|
||||||
import registerB from "./form/register.js";
|
import registerB from "./form/register.js";
|
||||||
|
@ -33,7 +34,8 @@ routes.client = {
|
||||||
user,
|
user,
|
||||||
settings,
|
settings,
|
||||||
e404,
|
e404,
|
||||||
tou
|
tou,
|
||||||
|
captcha
|
||||||
}
|
}
|
||||||
|
|
||||||
routes.get = {
|
routes.get = {
|
||||||
|
|
|
@ -270,5 +270,6 @@ img {
|
||||||
}
|
}
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
white-space: pre-wrap
|
white-space: pre-wrap;
|
||||||
|
font-family: var(--font) !important;
|
||||||
}
|
}
|
|
@ -3,6 +3,10 @@
|
||||||
<%= title %>
|
<%= title %>
|
||||||
</h1>
|
</h1>
|
||||||
<div class='form-message'></div>
|
<div class='form-message'></div>
|
||||||
|
<span class='form-key'>
|
||||||
|
<img src='/client/captcha'>
|
||||||
|
</span>
|
||||||
|
<input class='form-input' type='text' name='challenger' value='Type the captcha'>
|
||||||
<% for (let elem of data) { %>
|
<% for (let elem of data) { %>
|
||||||
<span class='form-key'>
|
<span class='form-key'>
|
||||||
<%= elem.label %>
|
<%= elem.label %>
|
||||||
|
|
Loading…
Reference in a new issue