Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 64 additions & 8 deletions apps/pdftk/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,68 @@ const PORT = process.env.PORT || '3000';
httpServer(
connect(
post({ path: '/compress' }, async ({ req, res }) => {
await compressStream({ input: req, output: res });
try {
await compressStream({ input: req, output: res });
} catch (error) {
if (!res.headersSent) {
res.statusCode = 500;
res.end(error instanceof Error ? error.message : 'pdftk failed');
}
}
}),
post({ path: '/uncompress' }, async ({ req, res }) => {
await uncompressStream({ input: req, output: res });
try {
await uncompressStream({ input: req, output: res });
} catch (error) {
if (!res.headersSent) {
res.statusCode = 500;
res.end(error instanceof Error ? error.message : 'pdftk failed');
}
}
}),
post(
{ path: '/encrypt' },
middlewareQuery(encryptSchema),
async ({ req, res, query: { password, userPassword, allow } }) => {
await encryptStream({ input: req, output: res, password, userPassword, allow });
try {
await encryptStream({ input: req, output: res, password, userPassword, allow });
} catch (error) {
if (!res.headersSent) {
res.statusCode = 500;
res.end(error instanceof Error ? error.message : 'pdftk failed');
}
}
},
),
post({ path: '/decrypt' }, middlewareQuery(decryptSchema), async ({ req, res, query: { password } }) => {
await decryptStream({ input: req, output: res, password });
try {
await decryptStream({ input: req, output: res, password });
} catch (error) {
if (!res.headersSent) {
res.statusCode = 500;
res.end(error instanceof Error ? error.message : 'pdftk failed');
}
}
}),
post({ path: '/data/fields' }, async ({ req, res }) => {
await dataFieldsStream({ input: req, output: res });
try {
await dataFieldsStream({ input: req, output: res });
} catch (error) {
if (!res.headersSent) {
res.statusCode = 500;
res.end(error instanceof Error ? error.message : 'pdftk failed');
}
}
}),
post({ path: '/data/dump' }, async ({ req, res }) => {
await dataDumpStream({ input: req, output: res });
try {
await dataDumpStream({ input: req, output: res });
} catch (error) {
if (!res.headersSent) {
res.statusCode = 500;
res.end(error instanceof Error ? error.message : 'pdftk failed');
}
}
}),
// post({ path: '/data/annots' }, async ({ req, res }) => {
// const pdftkSpawn = spawn('java', ['-jar', '/pdftk/pdftk.jar', '-', 'dump_data_annots', '-']);
Expand All @@ -57,11 +99,25 @@ httpServer(
{ path: '/form/fill' },
middlewareQuery(formFillSchema),
async ({ req, res, query: { flag, fontName, ...data } }) => {
await formFillStream({ input: req, output: res, flag, fontName, data });
try {
await formFillStream({ input: req, output: res, flag, fontName, data });
} catch (error) {
if (!res.headersSent) {
res.statusCode = 500;
res.end(error instanceof Error ? error.message : 'pdftk failed');
}
}
},
),
post({ path: '/data/fdf' }, async ({ req, res }) => {
await dataFdfStream({ input: req, output: res });
try {
await dataFdfStream({ input: req, output: res });
} catch (error) {
if (!res.headersSent) {
res.statusCode = 500;
res.end(error instanceof Error ? error.message : 'pdftk failed');
}
}
}),
...healthEndpoints,
),
Expand Down
9 changes: 8 additions & 1 deletion apps/tesseract/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@ httpServer(
connect(
post({ path: '/image-to-text' }, async ({ req, res }) => {
res.setHeader('Content-Type', 'text/plain');
imageToText({ input: req, output: res });
try {
await imageToText({ input: req, output: res });
} catch (error) {
if (!res.headersSent) {
res.statusCode = 500;
res.end(error instanceof Error ? error.message : 'tesseract failed');
}
}
}),
...healthEndpoints,
),
Expand Down
2 changes: 1 addition & 1 deletion libs/binary/tesseract/src/lib/image-to-text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { spawn } from 'node:child_process';
import type { Writable } from 'node:stream';
import { type InputType, streamChildProcess, streamChildProcessToBuffer } from '@container/stream';

export function imageToText(options: { input: InputType; output: Writable }): void;
export function imageToText(options: { input: InputType; output: Writable }): Promise<void>;
export function imageToText(options: { input: InputType }): Promise<Buffer>;
export function imageToText({ input, output }: { input: InputType; output?: Writable }) {
const tesseract = spawn('tesseract', ['-', '-']);
Expand Down
51 changes: 35 additions & 16 deletions libs/stream/src/lib/stream-child-process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,45 +31,64 @@ export async function streamChildProcess(
options?: StreamChildProcessOptions,
): Promise<void> {
const { end = true } = options ?? {};
const stderrChunks: Buffer[] = [];
child.stderr.on('data', (chunk) => {
stderrChunks.push(Buffer.from(chunk));
});

child.stderr.on('error', (error) => {
console.error(error);
});

child.stdin.on('error', (error) => {
console.error(error);
child.kill();
});

await streamInputToWriteable(input, child.stdin, { end: true });
try {
await streamInputToWriteable(input, child.stdin, { end: true });
} catch (error) {
child.kill();
const stderrOutput = Buffer.concat(stderrChunks).toString('utf-8');
if (stderrOutput) {
const message = error instanceof Error ? error.message : String(error);
throw new Error(`${message}: ${stderrOutput}`);
}
throw error;
}

child.stdout
.on('error', (error) => {
console.error(error);
})
.pipe(output, { end });

const stderrChunks: Buffer[] = [];
child.stderr.on('data', (chunk) => {
stderrChunks.push(Buffer.from(chunk));
});

child.stderr.on('error', (error) => {
console.error(error);
});
let stdoutFinished = false;

output.on('close', () => {
child.kill();
if (!stdoutFinished) {
child.kill();
}
});

// Set up exit listener before awaiting to avoid race condition
const exitPromise = new Promise<number | null>((resolve) => {
child.on('exit', (code) => {
resolve(code);
const exitPromise = new Promise<{ code: number | null; signal: NodeJS.Signals | null }>((resolve) => {
child.on('exit', (code, signal) => {
resolve({ code, signal });
});
});

await finished(child.stdout);
stdoutFinished = true;

// Wait for the process to exit and check the exit code
const exitCode = await exitPromise;
const { code, signal } = await exitPromise;

if (exitCode !== 0) {
if (code !== 0) {
const stderrOutput = Buffer.concat(stderrChunks).toString('utf-8');
throw new Error(`Child process exited with code ${exitCode}${stderrOutput ? `: ${stderrOutput}` : ''}`);
if (code === null && signal) {
throw new Error(`Child process exited with signal ${signal}${stderrOutput ? `: ${stderrOutput}` : ''}`);
}
throw new Error(`Child process exited with code ${code}${stderrOutput ? `: ${stderrOutput}` : ''}`);
}
}
Loading