Compare commits
2 Commits
c257844fa2
...
92fd491194
Author | SHA1 | Date |
---|---|---|
|
92fd491194 | |
|
991a4f29a6 |
|
@ -1,18 +0,0 @@
|
|||
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)
|
||||
)
|
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/env bash
|
||||
source .env
|
||||
DATABASE_URL=postgres://$DBUSER:$DBPASS@$DBHOST:$DBPORT npx node-pg-migrate $*
|
|
@ -0,0 +1,23 @@
|
|||
export const shorthands = undefined;
|
||||
|
||||
/**
|
||||
* @param pgm {import('node-pg-migrate').MigrationBuilder}
|
||||
* @param run {() => void | undefined}
|
||||
* @returns {Promise<void> | void}
|
||||
*/
|
||||
export function up(pgm) {
|
||||
pgm.createTable('captchas', {
|
||||
id: 'id',
|
||||
hash: {type: 'char(32)'},
|
||||
solution: {type: 'char(6)'}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pgm {import('node-pg-migrate').MigrationBuilder}
|
||||
* @param run {() => void | undefined}
|
||||
* @returns {Promise<void> | void}
|
||||
*/
|
||||
export function down(pgm) {
|
||||
pgm.dropTable('captchas')
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
export const shorthands = undefined;
|
||||
|
||||
/**
|
||||
* @param pgm {import('node-pg-migrate').MigrationBuilder}
|
||||
* @param run {() => void | undefined}
|
||||
* @returns {Promise<void> | void}
|
||||
*/
|
||||
export function up(pgm) {
|
||||
pgm.createType('protocol_type', ['http', 'https']);
|
||||
|
||||
pgm.createTable('proxies', {
|
||||
id: 'id',
|
||||
proto: {type: 'protocol_type', notNull: true},
|
||||
claimed: {type: 'boolean', default: false},
|
||||
host: {type: 'inet', notNull: true},
|
||||
port: {type: 'smallint', notNull: true},
|
||||
username: {type: 'varchar(16)'},
|
||||
password: {type: 'varchar(32)'}
|
||||
});
|
||||
|
||||
pgm.addColumn('captchas', {proxy_id: {type: 'integer', references: 'proxies(id)'}});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pgm {import('node-pg-migrate').MigrationBuilder}
|
||||
* @param run {() => void | undefined}
|
||||
* @returns {Promise<void> | void}
|
||||
*/
|
||||
export function down(pgm) {
|
||||
pgm.dropType('protocol_type');
|
||||
pgm.dropTable('proxies');
|
||||
pgm.dropColumn('captchas', 'proxy_id');
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
export const shorthands = undefined;
|
||||
|
||||
/**
|
||||
* @param pgm {import('node-pg-migrate').MigrationBuilder}
|
||||
* @param run {() => void | undefined}
|
||||
* @returns {Promise<void> | void}
|
||||
*/
|
||||
export function up(pgm) {
|
||||
pgm.createTable('users', {
|
||||
id: 'id',
|
||||
username: {type:"varchar(32)", notNull: true},
|
||||
password: {type:"char(60)", notNull: true}
|
||||
})
|
||||
|
||||
pgm.addColumn('captchas', {
|
||||
submitter: {type: 'integer', references: 'users(id)'}
|
||||
});
|
||||
|
||||
pgm.dropType('protocol_type', {cascade: true});
|
||||
pgm.dropTable('proxies', {cascade: true});
|
||||
pgm.dropColumn('captchas', 'proxy_id', {cascade: true});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pgm {import('node-pg-migrate').MigrationBuilder}
|
||||
* @param run {() => void | undefined}
|
||||
* @returns {Promise<void> | void}
|
||||
*/
|
||||
export function down(pgm) {
|
||||
pgm.dropTable('users');
|
||||
pgm.dropColumn('captchas', 'submitter');
|
||||
|
||||
pgm.createType('protocol_type', ['http', 'https']);
|
||||
|
||||
pgm.createTable('proxies', {
|
||||
id: 'id',
|
||||
proto: {type: 'protocol_type', notNull: true},
|
||||
claimed: {type: 'boolean', default: false},
|
||||
host: {type: 'inet', notNull: true},
|
||||
port: {type: 'smallint', notNull: true},
|
||||
username: {type: 'varchar(16)'},
|
||||
password: {type: 'varchar(32)'}
|
||||
});
|
||||
|
||||
pgm.addColumn('captchas', {proxy_id: {type: 'integer', references: 'proxies(id)'}});
|
||||
}
|
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
|
@ -11,15 +11,21 @@
|
|||
"type": "module",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
"start": "node src/index.js"
|
||||
"start": "node src/index.js",
|
||||
"migrate": "bash migrate.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"bcrypt": "^5.1.1",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"dotenv": "^16.5.0",
|
||||
"express": "^5.1.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"pg": "^8.15.5",
|
||||
"pug": "^3.0.3"
|
||||
"pug": "^3.0.3",
|
||||
"undici": "^7.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"node-pg-migrate": "^7.9.1",
|
||||
"nodemon": "^3.1.10"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,73 @@
|
|||
const check_solution = async (solution) => {
|
||||
const body = {
|
||||
"TotalSum": "78278",
|
||||
"FnNumber": "9960440301173139",
|
||||
"ReceiptOperationType": "1",
|
||||
"DocNumber": "35704",
|
||||
"DocFiscalSign": "4149689833",
|
||||
"Captcha": solution,
|
||||
"DocDateTime": "2022-09-21T20:28:00.000Z"
|
||||
}
|
||||
const result = await fetch("https://check.ofd.ru/Document/FetchReceiptFromFns", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json;charset=utf-8",
|
||||
// "Origin": "https://check.ofd.ru"
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
return result.status != 400;
|
||||
}
|
||||
|
||||
|
||||
const get_cookie = (name) => {
|
||||
const cookies = document.cookie.split(';');
|
||||
for (let i = 0; i < cookies.length; i++) {
|
||||
const cookie = cookies[i].trim();
|
||||
if (cookie.startsWith(name + '=')) {
|
||||
const value = cookie.substring(name.length + 1);
|
||||
console.log(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const blobToBase64 = (blob) => {
|
||||
return new Promise((resolve, _) => {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => resolve(reader.result);
|
||||
reader.readAsDataURL(blob);
|
||||
});
|
||||
}
|
||||
|
||||
window.onload = async () => {
|
||||
let id = (await (await fetch("/api/captcha", {method: "POST"})).json()).id
|
||||
console.log(id);
|
||||
fetch(`/api/captcha/${id}`).then(response => response.blob())
|
||||
.then(blob => {
|
||||
const url = URL.createObjectURL(blob);
|
||||
document.getElementById("captcha_image").src = url;
|
||||
}
|
||||
);
|
||||
console.log("koka: " + get_cookie("JWT"));
|
||||
console.log("all: " + document.cookie);
|
||||
if (!document.cookie.includes('JWT')) {
|
||||
document.location.href = "/login";
|
||||
}
|
||||
|
||||
const response = await fetch("https://check.ofd.ru/api/captcha/common/img");
|
||||
captcha = await response.blob();
|
||||
|
||||
const url = URL.createObjectURL(captcha);
|
||||
document.getElementById("captcha_image").src = url;
|
||||
console.log(captcha.type)
|
||||
const form = document.getElementById("captchaForm");
|
||||
const inputField = document.getElementById("captcha");
|
||||
form.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
// if (!await check_solution(inputField.value)) {
|
||||
// alert("Капча решена неверно")
|
||||
// returnl
|
||||
// }
|
||||
|
||||
const response = await fetch(`/api/captcha/${id}`, {method: "PATCH",headers: {'Content-Type': 'application/json'}, body: JSON.stringify({"solution": inputField.value})});
|
||||
const response = await fetch(`/api/captcha/submit`, {method: "POST", headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ "image": await blobToBase64(captcha), "solution": inputField.value})});
|
||||
if (response.status == 200) {
|
||||
inputField.value = "";
|
||||
window.location.reload();
|
||||
};
|
||||
})
|
||||
} else {
|
||||
response_json = await response.json()
|
||||
alert(response_json.message)
|
||||
}
|
||||
});
|
||||
};
|
|
@ -0,0 +1,48 @@
|
|||
const set_cookie = (name, value, days) => {
|
||||
const expires = new Date(Date.now() + days * 86400 * 1000).toUTCString();
|
||||
document.cookie = `${name}=${value}; expires=${expires}; path=/;`;
|
||||
}
|
||||
|
||||
window.onload = async () => {
|
||||
alert("This service requests a captcha from https://ofd.ru and sends an example receipt to it to check the correctness of the captcha. If you are not okay with making such requests, please leave the site immediately");
|
||||
|
||||
if (document.cookie.includes('JWT')) {
|
||||
document.location = '/';
|
||||
}
|
||||
|
||||
const form = document.getElementById("loginForm");
|
||||
const username = document.getElementById("username");
|
||||
const password = document.getElementById("password");
|
||||
form.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const response = await fetch(
|
||||
`/api/user/login`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
"username": username.value,
|
||||
"password": password.value
|
||||
})
|
||||
});
|
||||
switch (response.status) {
|
||||
case 403:
|
||||
alert("Incorrect password");
|
||||
password.value = "";
|
||||
break;
|
||||
case 200:
|
||||
response_json = await response.json()
|
||||
set_cookie("JWT", response_json["token"], 365);
|
||||
window.location.href = '/';
|
||||
break;
|
||||
case 404:
|
||||
alert("No such user exists");
|
||||
username.value = "";
|
||||
break;
|
||||
default:
|
||||
alert("Unknown server error. Please, conact the developer");
|
||||
console.log(response);
|
||||
}
|
||||
});
|
||||
};
|
|
@ -8,4 +8,6 @@ DBPASS=GENERATE_A_STRONG_PASSWORD_HERE
|
|||
#General
|
||||
APP_PORT=3000
|
||||
CAPTCHA_SOURCE_URL=https://example.com
|
||||
DATA_DIR=/opt/captcha_aggregator
|
||||
DATA_DIR=/opt/captcha_aggregator
|
||||
ADMIN_TOKEN=GENERATE_A_STRONG_TOKEN_HERE
|
||||
SECRET=GENERATE_A_STRONG_SECRET_HERE
|
|
@ -14,7 +14,9 @@ const config = {
|
|||
app_port: Number.parseInt(process.env.APP_PORT),
|
||||
|
||||
captcha_source_url: process.env.CAPTCHA_SOURCE_URL,
|
||||
data_dir: process.env.DATA_DIR
|
||||
data_dir: process.env.DATA_DIR,
|
||||
admin_token: process.env.ADMIN_TOKEN,
|
||||
secret: process.env.SECRET
|
||||
}
|
||||
|
||||
export default config;
|
|
@ -1,56 +1,18 @@
|
|||
import CaptchaService from "../services/captcha.js";
|
||||
import ProxyService from "../services/proxy.js";
|
||||
import config from '../config.js';
|
||||
import fs from 'fs/promises';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
class CaptchaController {
|
||||
async new(req, res) {
|
||||
async submit(req, res) {
|
||||
const {image, solution} = req.body;
|
||||
if (!image) return res.status(400).send({"message":"You must send image blob"});
|
||||
if (!solution || solution.length != 6) return res.status(400).send({"message":"You must send a valid solution"});
|
||||
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)
|
||||
await CaptchaService.new(image, solution, jwt.decode(req.token).id);
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
return res.status(500).send({"message": "Unknown server error"});
|
||||
console.log(`Error upon submitting: ${e}`);
|
||||
return res.status(500).send({"message": "Unknown server error. Please, contact the developer."});
|
||||
}
|
||||
}
|
||||
|
||||
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"});
|
||||
}
|
||||
return res.status(200).send({"message": "Success"});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
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();
|
|
@ -0,0 +1,39 @@
|
|||
import UserService from '../services/user.js';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import bcrypt from 'bcrypt';
|
||||
import config from '../config.js';
|
||||
|
||||
class UserController {
|
||||
async register(req, res) {
|
||||
try {
|
||||
const { username, password } = req.body;
|
||||
if (!username) return res.status(400).send({"message":"You must specify username"});
|
||||
if (!password) return res.status(400).send({"message":"You must specify password"});
|
||||
if (await UserService.get_by_username(username)) return res.status(409).send({"message":"Such user already exists!"})
|
||||
const id = await UserService.add(username, bcrypt.hashSync(password, 10));
|
||||
const token = jwt.sign({"username": username, "id": id}, config.secret);
|
||||
return res.status(200).send({"messae": "Success", "token": token});
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
return res.status(500).send({"message": "Unknown server error. Please, contact the developer"});
|
||||
}
|
||||
}
|
||||
|
||||
async login (req, res) {
|
||||
try {
|
||||
const { username, password } = req.body;
|
||||
const user = await UserService.get_by_username(username);
|
||||
if (!user) return res.status(404).send({"message":"No such user exists"});
|
||||
const hashed_password = user.password;
|
||||
if (!bcrypt.compareSync(password, hashed_password)) return res.status(403).send({"message":"Passwords dont match"});
|
||||
const token = jwt.sign({"username": username, "id": user.id}, config.secret);
|
||||
return res.status(200).send({"message": "Success", "token": token});
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
return res.status(500).send({"message": "Unknown server error"});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export default new UserController();
|
|
@ -15,7 +15,4 @@ const pool = new Pool({
|
|||
port: config.dbport
|
||||
});
|
||||
|
||||
pool.query(fs.readFileSync('./db_schema.psql').toString());
|
||||
|
||||
|
||||
export default pool;
|
|
@ -1,20 +1,24 @@
|
|||
import express from 'express';
|
||||
import path from 'path';
|
||||
import cookieParser from 'cookie-parser';
|
||||
|
||||
import CaptchaRouter from './routers/captcha.js';
|
||||
import FrontendRouter from './routers/frontend.js';
|
||||
|
||||
import config from './config.js';
|
||||
import UserRouter from './routers/user.js';
|
||||
|
||||
const app = express();
|
||||
|
||||
app.use(express.static(path.join('./public')));
|
||||
app.use(express.urlencoded({ extended: false }));
|
||||
app.use(express.json());
|
||||
app.use(cookieParser());
|
||||
|
||||
app.set('view engine', 'pug');
|
||||
|
||||
app.use('/api', CaptchaRouter);
|
||||
app.use('/api', UserRouter);
|
||||
app.use('/', FrontendRouter);
|
||||
|
||||
const server = app.listen(config.app_port, () => {
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import jwt from 'jsonwebtoken';
|
||||
import config from '../config.js';
|
||||
|
||||
const verify_admin_token = async (req, res, next) => {
|
||||
const header = req.headers['authorization'];
|
||||
if (!header) return res.status(400).send({"message": "No admin token supplied"});
|
||||
if (config.admin_token != header.split(' ')[1]) return res.status(403).send({"message":"Admin token is incorrect"});
|
||||
next();
|
||||
}
|
||||
|
||||
const verify_user_jwt = async (req, res, next) => {
|
||||
const header = req.headers['authorization'];
|
||||
let token = req.cookies.JWT? req.cookies.JWT : (header? header.split(' ')[1] : undefined)
|
||||
if (!token) return res.status(400).send({"message": "No authorization token supplied"});
|
||||
req.token = token;
|
||||
if (!jwt.verify(req.token, config.secret)) return res.status(403).send({"message":"Could not verify authorization token"});
|
||||
next();
|
||||
}
|
||||
|
||||
export default {verify_admin_token, verify_user_jwt};
|
|
@ -1,11 +1,10 @@
|
|||
import { Router } from 'express';
|
||||
|
||||
import CaptchaController from '../controllers/captcha.js';
|
||||
import auth from '../middlewares/auth.js';
|
||||
|
||||
const CaptchaRouter = new Router();
|
||||
|
||||
CaptchaRouter.post('/captcha', CaptchaController.new);
|
||||
CaptchaRouter.get('/captcha/:id', CaptchaController.get);
|
||||
CaptchaRouter.patch('/captcha/:id', CaptchaController.solve);
|
||||
CaptchaRouter.post('/captcha/submit', auth.verify_user_jwt, CaptchaController.submit);
|
||||
|
||||
export default CaptchaRouter;
|
|
@ -6,4 +6,8 @@ FrontendRouter.get('/', async (req, res) => {
|
|||
return res.render("index.pug");
|
||||
});
|
||||
|
||||
FrontendRouter.get('/login', async (req, res) => {
|
||||
return res.render("login.pug");
|
||||
});
|
||||
|
||||
export default FrontendRouter;
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
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;
|
|
@ -0,0 +1,11 @@
|
|||
import { Router } from 'express';
|
||||
|
||||
import UserController from '../controllers/user.js';
|
||||
import auth from '../middlewares/auth.js';
|
||||
|
||||
const UserRouter = new Router();
|
||||
|
||||
UserRouter.post('/user/register', auth.verify_admin_token, UserController.register);
|
||||
UserRouter.post('/user/login', UserController.login);
|
||||
|
||||
export default UserRouter;
|
|
@ -1,72 +1,25 @@
|
|||
import db from '../db.js';
|
||||
import fs from 'fs/promises';
|
||||
import { ProxyAgent } from 'undici';
|
||||
import config from '../config.js';
|
||||
import { createHash } from 'crypto';
|
||||
|
||||
function base64ToArrayBuffer(base64) {
|
||||
var binaryString = atob(base64);
|
||||
var bytes = new Uint8Array(binaryString.length);
|
||||
for (var i = 0; i < binaryString.length; i++) {
|
||||
bytes[i] = binaryString.charCodeAt(i);
|
||||
}
|
||||
return bytes.buffer;
|
||||
}
|
||||
|
||||
class CaptchaService {
|
||||
async new(proxy) {
|
||||
try {
|
||||
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, proxy_id) VALUES ($1, $2) RETURNING id", [hash, proxy.id])).rows[0].id;
|
||||
|
||||
return id
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
async check_solution(solution) {
|
||||
const body = {
|
||||
"TotalSum": "78278",
|
||||
"FnNumber": "9960440301173139",
|
||||
"ReceiptOperationType": "1",
|
||||
"DocNumber": "35704",
|
||||
"DocFiscalSign": "4149689833",
|
||||
"Captcha": solution,
|
||||
"DocDateTime": "2022-09-21T20:28:00.000Z"
|
||||
}
|
||||
const result = await fetch("https://check.ofd.ru/Document/FetchReceiptFromFns", {
|
||||
"headers": {
|
||||
"Content-Type": "application/json;charset=utf-8"
|
||||
},
|
||||
"body": JSON.stringify(body),
|
||||
"method": "POST",
|
||||
});
|
||||
return result.status != 400;
|
||||
}
|
||||
|
||||
async get (id) {
|
||||
let result = await db.query("SELECT hash FROM captchas WHERE id = $1", [id])
|
||||
if (result.rows[0] == undefined) {
|
||||
return undefined
|
||||
}
|
||||
return result.rows[0].hash;
|
||||
}
|
||||
|
||||
async check_and_save_solution (id, solution) {
|
||||
if (!await this.check_solution(solution)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
async new(image, solution, submitter) {
|
||||
const b64 = image.replace(/^data:image\/jpeg;base64,/, "");
|
||||
const arrayBuffer = base64ToArrayBuffer(b64);
|
||||
const buffer = Buffer.from(arrayBuffer);
|
||||
const hash = createHash('md5').update(buffer).digest('hex');
|
||||
await fs.writeFile(`${config.data_dir}/${hash}.jpeg`, b64, 'base64');
|
||||
await db.query("INSERT INTO captchas (hash, solution, submitter) VALUES ($1, $2, $3)", [hash, solution, submitter]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
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();
|
|
@ -0,0 +1,13 @@
|
|||
import db from '../db.js';
|
||||
|
||||
class UserService {
|
||||
async add(username, password) {
|
||||
return (await db.query("INSERT INTO users (username, password) VALUES ($1, $2) RETURNING id", [username, password])).rows[0].id;
|
||||
}
|
||||
|
||||
async get_by_username(username) {
|
||||
return (await db.query("SELECT * FROM users WHERE username = $1", [username])).rows[0];
|
||||
}
|
||||
}
|
||||
|
||||
export default new UserService();
|
|
@ -0,0 +1,23 @@
|
|||
html
|
||||
head
|
||||
title Captcha Aggregator Login
|
||||
meta(charset="utf-8")
|
||||
meta(name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no")
|
||||
meta(name="description" content="")
|
||||
link(href="css/index.css" rel="stylesheet")
|
||||
body
|
||||
div(id="tsparticles")
|
||||
main(class="box")
|
||||
h2 Captcha Aggregator
|
||||
|
||||
form(id="loginForm")
|
||||
div(class="inputBox")
|
||||
label(for="username") Username
|
||||
input(id="username" placeholder="username" required=true)
|
||||
div(class="inputBox")
|
||||
label(for="password") Password
|
||||
input(type="password" name="password" id="password" placeholder="password" required=true)
|
||||
div
|
||||
button(type="submit" name="" style="float: center;") Log in
|
||||
script(type="text/javascript" src="js/login.js")
|
||||
|
Loading…
Reference in New Issue