Add packages/captcha (untested)
This commit is contained in:
parent
0ae8752e4d
commit
cd8044d6a9
@ -20,7 +20,8 @@
|
|||||||
"@rondo.dev/validator": "file:packages/validator",
|
"@rondo.dev/validator": "file:packages/validator",
|
||||||
"@rondo.dev/db": "file:packages/db",
|
"@rondo.dev/db": "file:packages/db",
|
||||||
"@rondo.dev/db-typeorm": "file:packages/db-typeorm",
|
"@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": {
|
"devDependencies": {
|
||||||
"@types/bcrypt": "^3.0.0",
|
"@types/bcrypt": "^3.0.0",
|
||||||
@ -125,4 +126,4 @@
|
|||||||
"watchify": "^3.11.1"
|
"watchify": "^3.11.1"
|
||||||
},
|
},
|
||||||
"name": "node"
|
"name": "node"
|
||||||
}
|
}
|
||||||
16
packages/captcha/jest.config.js
Normal file
16
packages/captcha/jest.config.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
module.exports = {
|
||||||
|
roots: [
|
||||||
|
'<rootDir>/src',
|
||||||
|
],
|
||||||
|
transform: {
|
||||||
|
'^.+\\.tsx?$': 'ts-jest',
|
||||||
|
},
|
||||||
|
testRegex: '(/__tests__/.*|\\.(test|spec))\\.tsx?$',
|
||||||
|
moduleFileExtensions: [
|
||||||
|
'ts',
|
||||||
|
'tsx',
|
||||||
|
'js',
|
||||||
|
'jsx',
|
||||||
|
],
|
||||||
|
setupFiles: ['<rootDir>/jest.setup.js'],
|
||||||
|
}
|
||||||
4
packages/captcha/jest.setup.js
Normal file
4
packages/captcha/jest.setup.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
if (!process.env.LOG) {
|
||||||
|
process.env.LOG = 'sql:warn'
|
||||||
|
}
|
||||||
|
process.chdir(__dirname)
|
||||||
15
packages/captcha/package.json
Normal file
15
packages/captcha/package.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
84
packages/captcha/src/audio.ts
Normal file
84
packages/captcha/src/audio.ts
Normal file
@ -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<ReadableWritable> {
|
||||||
|
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 })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
10
packages/captcha/src/image.ts
Normal file
10
packages/captcha/src/image.ts
Normal file
@ -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)
|
||||||
|
}
|
||||||
1
packages/captcha/src/index.ts
Normal file
1
packages/captcha/src/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './image'
|
||||||
8
packages/captcha/tsconfig.esm.json
Normal file
8
packages/captcha/tsconfig.esm.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "esm"
|
||||||
|
},
|
||||||
|
"references": [
|
||||||
|
]
|
||||||
|
}
|
||||||
9
packages/captcha/tsconfig.json
Normal file
9
packages/captcha/tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"extends": "../tsconfig.common.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "lib",
|
||||||
|
"rootDir": "src"
|
||||||
|
},
|
||||||
|
"references": [
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user