diff --git a/src/s3/s3.service.ts b/src/s3/s3.service.ts index 87c9b13e..11077048 100644 --- a/src/s3/s3.service.ts +++ b/src/s3/s3.service.ts @@ -233,6 +233,15 @@ export class S3Service implements OnModuleInit { }); } + async generateUploadUrl(key: string, isPublic: boolean): Promise { + const s3Client = isPublic ? this.s3PublicClient : this.s3Client; + const command = new PutObjectCommand({ + Bucket: this.bucket, + Key: key, + }); + return await getSignedUrl(s3Client, command, { expiresIn: 900 }); + } + async generateDownloadUrl( key: string, isPublic: boolean, diff --git a/src/tracing.ts b/src/tracing.ts index 6ba8dfdf..9602309e 100644 --- a/src/tracing.ts +++ b/src/tracing.ts @@ -44,7 +44,7 @@ if (isTracingEnabled()) { instrumentations: [ new HttpInstrumentation({ ignoreIncomingRequestHook: (req) => { - return excludedUrls.some((url) => req.url?.includes(url)) || false; + return excludedUrls.some((url) => req.url === url) || false; }, }), new ExpressInstrumentation({ diff --git a/src/wizard/dto/task-callback.dto.ts b/src/wizard/dto/task-callback.dto.ts index 4ade1128..e1c9baa4 100644 --- a/src/wizard/dto/task-callback.dto.ts +++ b/src/wizard/dto/task-callback.dto.ts @@ -9,8 +9,8 @@ export class TaskCallbackDto { id: string; @IsOptional() - exception: Record; + exception?: Record; @IsOptional() - output: Record; + output?: Record; } diff --git a/src/wizard/internal.wizard.controller.ts b/src/wizard/internal.wizard.controller.ts index f1093fdb..3f48a444 100644 --- a/src/wizard/internal.wizard.controller.ts +++ b/src/wizard/internal.wizard.controller.ts @@ -5,7 +5,7 @@ import { transformKeysToSnakeCase } from 'omniboxd/interceptor/utils'; import { TaskCallbackDto } from 'omniboxd/wizard/dto/task-callback.dto'; import { ChunkCallbackDto } from 'omniboxd/wizard/dto/chunk-callback.dto'; import { ChunkManagerService } from 'omniboxd/wizard/chunk-manager.service'; -import { Body, Controller, Get, Post, Query, Res } from '@nestjs/common'; +import { Body, Controller, Get, Param, Post, Query, Res } from '@nestjs/common'; import { FetchTaskRequest } from 'omniboxd/wizard/dto/fetch-task-request.dto'; @Controller('internal/api/v1/wizard') @@ -33,6 +33,19 @@ export class InternalWizardController { return await this.wizardService.taskDoneCallback(taskCallback); } + @Public() + @Post('tasks/:taskId/upload') + async createTaskResult(@Param('taskId') taskId: string) { + const url = await this.wizardService.createTaskUploadUrl(taskId); + return { url }; + } + + @Public() + @Post('tasks/:taskId/callback') + async handleUploadedTaskCallback(@Param('taskId') taskId: string) { + return await this.wizardService.uploadedTaskDoneCallback(taskId); + } + @Public() @Post('callback/chunk') async handleChunkCallback( diff --git a/src/wizard/wizard.service.ts b/src/wizard/wizard.service.ts index 92b598bd..e26c3cb6 100644 --- a/src/wizard/wizard.service.ts +++ b/src/wizard/wizard.service.ts @@ -29,6 +29,7 @@ import { isEmpty } from 'omniboxd/utils/is-empty'; import { FetchTaskRequest } from 'omniboxd/wizard/dto/fetch-task-request.dto'; import { S3Service } from 'omniboxd/s3/s3.service'; import { createGunzip } from 'zlib'; +import { buffer } from 'node:stream/consumers'; import { SharedResourcesService } from 'omniboxd/shared-resources/shared-resources.service'; import { ResourcesService } from 'omniboxd/resources/resources.service'; @@ -227,6 +228,23 @@ export class WizardService { return { task_id: task.id, resource_id: resource.id }; } + async createTaskUploadUrl(taskId: string): Promise { + return await this.s3Service.generateUploadUrl( + `wizard-tasks/${taskId}`, + true, + ); + } + + async uploadedTaskDoneCallback(taskId: string) { + const key = `wizard-tasks/${taskId}`; + const { stream } = await this.s3Service.getObject(key); + const payload = await buffer(stream); + const taskCallback: TaskCallbackDto = JSON.parse(payload.toString('utf-8')); + const result = await this.taskDoneCallback(taskCallback); + await this.s3Service.deleteObject(key); + return result; + } + async taskDoneCallback(data: TaskCallbackDto) { const task = await this.wizardTaskService.taskRepository.findOneOrFail({ where: { id: data.id }, @@ -244,8 +262,8 @@ export class WizardService { } task.endedAt = new Date(); - task.exception = data.exception; - task.output = data.output; + task.exception = data.exception || null; + task.output = data.output || null; await this.preprocessTask(task); await this.wizardTaskService.taskRepository.save(task);