import { Injectable } from '@nestjs/common'; import * as Joi from 'joi'; import { AbstractActionHandler, Format } from '../abstract-action.handler'; import { Action } from '../action.enum'; import { RequestContext } from '../_context/request.context'; import { S3Service } from './s3.service'; type QueryParams = { Bucket: string; Prefix?: string; MaxKeys?: number; 'list-type'?: string; }; @Injectable() export class ListObjectsV2Handler extends AbstractActionHandler { constructor(private readonly s3Service: S3Service) { super(); } format = Format.Xml; action = Action.S3ListObjectsV2; validator = Joi.object({ Bucket: Joi.string().required(), Prefix: Joi.string().default(''), MaxKeys: Joi.number().default(1000), 'list-type': Joi.string().optional(), }); protected async handle({ Bucket, Prefix, MaxKeys }: QueryParams, { awsProperties }: RequestContext) { const { objects, isTruncated } = await this.s3Service.listObjects(Bucket, Prefix!, MaxKeys!); const result: any = { Name: Bucket, Prefix: Prefix, MaxKeys: MaxKeys, IsTruncated: isTruncated, KeyCount: objects.length, }; // Only include Contents if there are objects, otherwise omit it // AWS SDK will interpret missing Contents as empty array in some versions if (objects.length > 0) { result.Contents = objects.map(obj => ({ Key: obj.key, LastModified: obj.updatedAt.toISOString(), ETag: `"${obj.etag}"`, Size: obj.size, StorageClass: obj.storageClass, })); } return result; } }