Picsur/backend/src/workers/sharp/sharp.worker.ts

124 lines
2.9 KiB
TypeScript

import { FileType } from 'picsur-shared/dist/dto/mimes.dto';
import { setrlimit } from 'posix.js';
import { Sharp } from 'sharp';
import {
SharpWorkerFinishOptions,
SharpWorkerInitMessage,
SharpWorkerOperationMessage,
SharpWorkerRecieveMessage,
SharpWorkerSendMessage,
} from './sharp.message';
import { UniversalSharpIn, UniversalSharpOut } from './universal-sharp';
export class SharpWorker {
private startTime = 0;
private sharpi: Sharp | null = null;
constructor() {
this.setup();
}
private setup() {
if (process.send === undefined) {
return this.purge('This is not a worker process');
}
const memoryLimit = parseInt(process.env['MEMORY_LIMIT_MB'] ?? '');
if (isNaN(memoryLimit) || memoryLimit <= 0) {
return this.purge('MEMORY_LIMIT_MB environment variable is not set');
}
setrlimit('data', {
soft: 1000 * 1000 * memoryLimit,
hard: 1000 * 1000 * memoryLimit,
});
process.on('message', this.messageHandler.bind(this));
this.sendMessage({
type: 'ready',
});
}
private messageHandler(message: SharpWorkerSendMessage): void {
if (message.type === 'init') {
this.init(message);
} else if (message.type === 'operation') {
this.operation(message);
} else if (message.type === 'finish') {
this.finish(message.filetype, message.options);
} else {
return this.purge('Unknown message type');
}
}
private init(message: SharpWorkerInitMessage): void {
if (this.sharpi !== null) {
return this.purge('Already initialized');
}
this.startTime = Date.now();
this.sharpi = UniversalSharpIn(
message.image,
message.filetype,
message.options,
);
}
private operation(message: SharpWorkerOperationMessage): void {
if (this.sharpi === null) {
return this.purge('Not initialized');
}
const operation = message.operation;
message.operation.parameters;
this.sharpi = (this.sharpi[operation.name] as any)(...operation.parameters);
}
private async finish(
filetype: FileType,
options: SharpWorkerFinishOptions,
): Promise<void> {
if (this.sharpi === null) {
return this.purge('Not initialized');
}
const sharpi = this.sharpi;
this.sharpi = null;
try {
const result = await UniversalSharpOut(sharpi, filetype, options);
const processingTime = Date.now() - this.startTime;
this.sendMessage({
type: 'result',
processingTime,
result,
});
} catch (e) {
return this.purge(e);
}
}
private sendMessage(message: SharpWorkerRecieveMessage): void {
if (process.send === undefined) {
return this.purge('This is not a worker process');
}
process.send(message);
}
private purge(reason: any): void {
if (typeof reason === 'string') {
console.error(new Error(reason));
} else {
console.error(reason);
}
process.exit(1);
}
}
new SharpWorker();