staging & TODO

This commit is contained in:
leca 2025-04-26 01:57:11 +03:00
parent 7ca23a2ff4
commit c257844fa2
12 changed files with 171 additions and 57 deletions

5
TODO Normal file
View File

@ -0,0 +1,5 @@
[ ] Fully implement and test proxies
[ ] Add a counter of tries for a captcha
[ ] Make new setting "max tries per captcha"
[ ] Unclaim proxy and delete captcha on max tries reached
[ ] Add retry to fronted when captcha is entered incorrectly

View File

@ -1,5 +1,18 @@
CREATE TABLE IF NOT EXISTS captchas(
CREATE TABLE IF NOT EXISTS captchas (
id SERIAL PRIMARY KEY,
proxy_id INTEGER REFERENCES proxies(id),
hash CHAR(32),
solution CHAR(6)
);
);
CREATE TYPE IF NOT EXISTS protocol_type AS ENUM ('http', 'https');
CREATE TABLE IF NOT EXISTS proxies (
id SERIAL PRIMARY KEY,
proto protocol_type,
claimed BOOLEAN DEFAULT FALSE,
host INET NOT NULL,
port SMALLINT NOT NULL,
username VARCHAR(16),
password VARCHAR(32)
)

View File

@ -1,5 +1,5 @@
window.onload = async () => {
let id = (await (await fetch("/api/captcha/new", {method: "POST"})).json()).id
let id = (await (await fetch("/api/captcha", {method: "POST"})).json()).id
console.log(id);
fetch(`/api/captcha/${id}`).then(response => response.blob())
.then(blob => {

View File

@ -1,38 +0,0 @@
import CaptchaService from "../services/captcha.js";
import config from '../config.js';
import fs from 'fs/promises';
class ApiController {
async new(req, res) {
const id = await CaptchaService.new();
return res.status(200).send({"id": id});
}
async get(req, res) {
const id = req.params.id;
const hash = await CaptchaService.get(id);
if (hash == undefined) {
return res.status(404).send({"message": "captcha not found"});
}
const image = await fs.readFile(`${config.data_dir}/${hash}.jpeg`);
return res.status(200).send(image)
}
async solve (req, res) {
const id = req.params.id;
const solution = req.body["solution"];
if (solution == undefined || solution.length != 6) {
return res.status(400).send({"message": 'please, send a valid solution. Example: {"solution":"123456"}'});
}
if (!await CaptchaService.check_and_save_solution(id, solution))
return res.status(409).send({"message": "Solution is not correct"});
return res.status(200).send({"message": "Successful"});
}
}
export default new ApiController();

View File

@ -0,0 +1,57 @@
import CaptchaService from "../services/captcha.js";
import ProxyService from "../services/proxy.js";
import config from '../config.js';
import fs from 'fs/promises';
class CaptchaController {
async new(req, res) {
try {
const proxy = await ProxyService.take();
const id = await CaptchaService.new(proxy);
return res.status(200).send({"id": id});
} catch (e) {
console.log(e)
return res.status(500).send({"message": "Unknown server error"});
}
}
async get(req, res) {
try {
const id = req.params.id;
const hash = await CaptchaService.get(id);
if (hash == undefined) {
return res.status(404).send({"message": "captcha not found"});
}
const image = await fs.readFile(`${config.data_dir}/${hash}.jpeg`);
return res.status(200).send(image)
} catch (e) {
console.log(e)
return res.status(500).send({"message": "Unknown server error"});
}
}
async solve (req, res) {
try {
const id = req.params.id;
const solution = req.body["solution"];
if (solution == undefined || solution.length != 6) {
return res.status(400).send({"message": 'please, send a valid solution. Example: {"solution":"123456"}'});
}
if (!await CaptchaService.check_and_save_solution(id, solution))
return res.status(409).send({"message": "Solution is not correct"});
let proxy_id = await CaptchaService.get_assigned_proxy(id);
await ProxyService.give_back(id);
return res.status(200).send({"message": "Successful"});
} catch (e) {
console.log(e)
return res.status(500).send({"message": "Unknown server error"});
}
}
}
export default new CaptchaController();

31
src/controllers/proxy.js Normal file
View File

@ -0,0 +1,31 @@
import ProxyService from '../services/proxy.js';
class CaptchaController {
async add(req, res) {
try {
const { host, port, user: username, password } = req.body;
if (!host || !port) return res.status(400).send({"message":"You must specify host and port!"});
const id = await ProxyService.add(host, port, username, password);
return res.status(200).send({"id": id});
} catch (e) {
console.log(e)
return res.status(500).send({"message": "Unknown server error"});
}
}
async delete (req, res) {
try {
const id = req.params.id;
await ProxyService.delete(id)
return res.status(200).send({"message": "Successful"});
} catch (e) {
console.log(e)
return res.status(500).send({"message": "Unknown server error"});
}
}
}
export default new CaptchaController();

View File

@ -1,7 +1,7 @@
import express from 'express';
import path from 'path';
import ApiRouter from './routers/api.js';
import CaptchaRouter from './routers/captcha.js';
import FrontendRouter from './routers/frontend.js';
import config from './config.js';
@ -14,7 +14,7 @@ app.use(express.json());
app.set('view engine', 'pug');
app.use('/api', ApiRouter);
app.use('/api', CaptchaRouter);
app.use('/', FrontendRouter);
const server = app.listen(config.app_port, () => {

View File

@ -1,11 +0,0 @@
import { Router } from 'express';
import ApiController from '../controllers/api.js';
const ApiRouter = new Router();
ApiRouter.post('/captcha/new', ApiController.new);
ApiRouter.get('/captcha/:id', ApiController.get);
ApiRouter.patch('/captcha/:id', ApiController.solve);
export default ApiRouter;

11
src/routers/captcha.js Normal file
View File

@ -0,0 +1,11 @@
import { Router } from 'express';
import CaptchaController from '../controllers/captcha.js';
const CaptchaRouter = new Router();
CaptchaRouter.post('/captcha', CaptchaController.new);
CaptchaRouter.get('/captcha/:id', CaptchaController.get);
CaptchaRouter.patch('/captcha/:id', CaptchaController.solve);
export default CaptchaRouter;

11
src/routers/proxy.js Normal file
View File

@ -0,0 +1,11 @@
import { Router } from 'express';
import ProxyController from '../controllers/proxy.js';
const ProxyRouter = new Router();
ProxyRouter.post('/proxy', ProxyController.add);
ProxyRouter.get('/proxy/:id', ProxyController.get);
ProxyRouter.patch('/captcha/:id', ProxyController.solve);
export default ProxyRouter;

View File

@ -1,18 +1,26 @@
import db from '../db.js';
import fs from 'fs/promises';
import { ProxyAgent } from 'undici';
import config from '../config.js';
import { createHash } from 'crypto';
class CaptchaService {
async new() {
async new(proxy) {
try {
const captcha = await (await fetch(config.captcha_source_url)).blob();
let dispatcher;
if (proxy.username) {
dispatcher = new ProxyAgent(`${proxy.proto}://${proxy.username}:${proxy.password}@${proxy.host}:${proxy.port}`)
} else {
dispatcher = new ProxyAgent(`${proxy.proto}://${proxy.host}:${proxy.port}`)
}
const captcha = await (await fetch(config.captcha_source_url, dispatcher)).blob();
const buffer = Buffer.from(await captcha.arrayBuffer());
const hash = createHash('md5').update(buffer).digest('hex');
await fs.writeFile(`${config.data_dir}/${hash}.jpeg`, buffer);
const id = (await db.query("INSERT INTO captchas (hash) VALUES ($1) RETURNING id", [hash])).rows[0].id;
const id = (await db.query("INSERT INTO captchas (hash, proxy_id) VALUES ($1, $2) RETURNING id", [hash, proxy.id])).rows[0].id;
return id
} catch(e) {
@ -56,6 +64,10 @@ class CaptchaService {
await db.query("UPDATE captchas SET solution = $1 WHERE id = $2", [solution, id]);
return true;
}
async get_assigned_proxy(id) {
return (await db.query("SELECT proxy_id FROM captchas WHERE id = $1", [id])).rows[0].proxy_id;
}
}
export default new CaptchaService();

23
src/services/proxy.js Normal file
View File

@ -0,0 +1,23 @@
import db from '../db.js';
class ProxyService {
async add(proto, host, port, username, password) {
await db.query("INSERT INTO proxies (proto, host, port, username, password) VALUES ($1, $2, $3, $4, $5)", [proto, host, port, username, password]);
}
async take() {
let proxy = (await db.query("UPDATE proxies SET claimed = TRUE WHERE id = ( SELECT id FROM proxies WHERE claimed = FALSE ORDER BY id LIMIT 1 ) RETURNING proto, host, port, username, password")).rows[0]
if (proxy == undefined) return undefined;
return proxy;
}
async give_back(id) {
await db.query("UPDATE proxies SET claimed = false WHERE id = $1", [id]);
}
async delete(id) {
await db.query("DELETE FRM proxies WHERE id = $1", [id]);
}
}
export default new ProxyService();