From cd8044d6a9644f9cdc0e848f031d1b65e68adead Mon Sep 17 00:00:00 2001 From: Jerko Steiner Date: Tue, 22 Oct 2019 21:39:39 -0500 Subject: [PATCH] Add packages/captcha (untested) --- package.json | 5 +- packages/captcha/jest.config.js | 16 ++++++ packages/captcha/jest.setup.js | 4 ++ packages/captcha/package.json | 15 ++++++ packages/captcha/src/audio.ts | 84 ++++++++++++++++++++++++++++++ packages/captcha/src/image.ts | 10 ++++ packages/captcha/src/index.ts | 1 + packages/captcha/tsconfig.esm.json | 8 +++ packages/captcha/tsconfig.json | 9 ++++ 9 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 packages/captcha/jest.config.js create mode 100644 packages/captcha/jest.setup.js create mode 100644 packages/captcha/package.json create mode 100644 packages/captcha/src/audio.ts create mode 100644 packages/captcha/src/image.ts create mode 100644 packages/captcha/src/index.ts create mode 100644 packages/captcha/tsconfig.esm.json create mode 100644 packages/captcha/tsconfig.json diff --git a/package.json b/package.json index e0f19f9..67cdd32 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,8 @@ "@rondo.dev/validator": "file:packages/validator", "@rondo.dev/db": "file:packages/db", "@rondo.dev/db-typeorm": "file:packages/db-typeorm", - "@rondo.dev/middleware": "file:packages/middleware" + "@rondo.dev/middleware": "file:packages/middleware", + "@rondo.dev/captcha": "file:packages/captcha" }, "devDependencies": { "@types/bcrypt": "^3.0.0", @@ -125,4 +126,4 @@ "watchify": "^3.11.1" }, "name": "node" -} +} \ No newline at end of file diff --git a/packages/captcha/jest.config.js b/packages/captcha/jest.config.js new file mode 100644 index 0000000..4c0cf86 --- /dev/null +++ b/packages/captcha/jest.config.js @@ -0,0 +1,16 @@ +module.exports = { + roots: [ + '/src', + ], + transform: { + '^.+\\.tsx?$': 'ts-jest', + }, + testRegex: '(/__tests__/.*|\\.(test|spec))\\.tsx?$', + moduleFileExtensions: [ + 'ts', + 'tsx', + 'js', + 'jsx', + ], + setupFiles: ['/jest.setup.js'], +} diff --git a/packages/captcha/jest.setup.js b/packages/captcha/jest.setup.js new file mode 100644 index 0000000..a952c9b --- /dev/null +++ b/packages/captcha/jest.setup.js @@ -0,0 +1,4 @@ +if (!process.env.LOG) { + process.env.LOG = 'sql:warn' +} +process.chdir(__dirname) diff --git a/packages/captcha/package.json b/packages/captcha/package.json new file mode 100644 index 0000000..90dfdaf --- /dev/null +++ b/packages/captcha/package.json @@ -0,0 +1,15 @@ +{ + "name": "@rondo.dev/captcha", + "version": "0.0.1", + "private": true, + "scripts": { + "test": "jest", + "lint": "tslint --project .", + "compile": "tsc", + "clean": "rm -rf lib/" + }, + "dependencies": {}, + "main": "lib/index.js", + "module": "esm/index.js", + "types": "lib/index.d.ts" +} \ No newline at end of file diff --git a/packages/captcha/src/audio.ts b/packages/captcha/src/audio.ts new file mode 100644 index 0000000..42d9ae9 --- /dev/null +++ b/packages/captcha/src/audio.ts @@ -0,0 +1,84 @@ +import { spawn } from 'child_process' +import { Request, Response } from 'express' +import { Readable } from 'stream' + +export async function audio(req: Request, res: Response) { + // TODO generate random string + const captcha = 'test' + req.session!.captcha = captcha + const speech = await speak('test') + res.type(speech.contentType) + speech.stdout.pipe(res) +} + +async function speak(text: string) { + const streams: ReadableWritable[] = [ + await espeak(), + await opus(), + ] + + const last = streams.reduce((prev, proc) => { + prev.stdout.pipe(proc.stdin) + return proc + }, createTextStream(text)) + + return last +} + +interface ReadableProcess { + stdout: NodeJS.ReadableStream + contentType: string +} + +interface WritableProcess { + stdin: NodeJS.WritableStream +} + +interface ReadableWritable extends ReadableProcess, WritableProcess { + +} + +export function espeak() { + return run( + 'espeak', + ['-k', '2', '-s', '90', '--stdin', '--stdout'], + 'audio/wav', + ) +} + +async function opus() { + return run('opusenc', ['-', '-'], 'audio/opus') +} + +class TextStream extends Readable { + constructor(text: string) { + super() + this.push(text) + this.push(null) + } + _read() {/* noop */} +} + +function createTextStream(text: string): ReadableProcess { + return { + stdout: new TextStream(text), + contentType: 'text/plain', + } +} + +async function run( + cmd: string, args: string[], contentType: string, +): Promise { + return new Promise((resolve, reject) => { + const p = spawn(cmd, args) + + p.once('error', err => { + console.error(err.stack) + reject(err) + }) + + if (p.pid) { + resolve({ stdin: p.stdin, stdout: p.stdout, contentType }) + } + }) +} diff --git a/packages/captcha/src/image.ts b/packages/captcha/src/image.ts new file mode 100644 index 0000000..2bdb179 --- /dev/null +++ b/packages/captcha/src/image.ts @@ -0,0 +1,10 @@ +import SVGCaptcha from 'svg-captcha' +import { Request, Response } from 'express' + +export function image(req: Request, res: Response) { + const { text, data } = SVGCaptcha.create() + req.session!.captcha = text + res.type('svg') + res.status(200) + res.send(data) +} diff --git a/packages/captcha/src/index.ts b/packages/captcha/src/index.ts new file mode 100644 index 0000000..556dbfd --- /dev/null +++ b/packages/captcha/src/index.ts @@ -0,0 +1 @@ +export * from './image' diff --git a/packages/captcha/tsconfig.esm.json b/packages/captcha/tsconfig.esm.json new file mode 100644 index 0000000..6e2aaa7 --- /dev/null +++ b/packages/captcha/tsconfig.esm.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "esm" + }, + "references": [ + ] +} diff --git a/packages/captcha/tsconfig.json b/packages/captcha/tsconfig.json new file mode 100644 index 0000000..94e864b --- /dev/null +++ b/packages/captcha/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../tsconfig.common.json", + "compilerOptions": { + "outDir": "lib", + "rootDir": "src" + }, + "references": [ + ] +}