fix: 修正Blog Live2D缺失资源状态码

This commit is contained in:
sunlei 2026-07-04 10:02:41 +08:00
parent 453d95ef35
commit 2a30049600
2 changed files with 56 additions and 5 deletions

View File

@ -1,4 +1,8 @@
import { BadRequestException, Injectable } from '@nestjs/common';
import {
BadRequestException,
Injectable,
NotFoundException,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { MinioClientService } from './asset-minio.service';
import type {
@ -11,6 +15,24 @@ const DEFAULT_LIVE2D_BUCKET = 'kt-template-online';
const DEFAULT_LIVE2D_PREFIX = 'blog/live2d/pio';
const MAX_DECODE_DEPTH = 6;
/**
* Detects MinIO/S3 object-missing errors from `statObject` and `getObject`.
* @param error - Unknown failure thrown by the MinIO client while resolving a runtime object.
* @returns `true` when the failure means the requested object key does not exist.
*/
function isMinioObjectNotFound(error: unknown): boolean {
if (!error || typeof error !== 'object') {
return false;
}
const candidate = error as { code?: unknown; message?: unknown };
return (
candidate.code === 'NotFound' ||
candidate.code === 'NoSuchKey' ||
candidate.message === 'Not Found'
);
}
@Injectable()
export class BlogLive2DAssetService {
/**
@ -54,10 +76,17 @@ export class BlogLive2DAssetService {
version: string,
objectPath: BlogLive2DRuntimeAssetPath,
): Promise<BlogLive2DAssetResult> {
return this.minioClientService.getObject(
try {
return await this.minioClientService.getObject(
this.resolveRuntimeObjectPath(version, objectPath),
this.getBucketName(),
);
} catch (error) {
if (isMinioObjectNotFound(error)) {
throw new NotFoundException('Live2D runtime asset not found');
}
throw error;
}
}
/**

View File

@ -117,6 +117,28 @@ describe('BlogLive2DAssetService', () => {
);
});
it('maps missing MinIO runtime objects to HTTP 404', async () => {
const minio = createMinio();
minio.getObject.mockRejectedValueOnce(
Object.assign(new Error('Not Found'), { code: 'NotFound' }),
);
const service = new BlogLive2DAssetService(
minio as never,
createConfig() as never,
);
await expect(
service.getRuntimeObject('v1', ['manifest.json']),
).rejects.toMatchObject({
status: HttpStatus.NOT_FOUND,
message: 'Live2D runtime asset not found',
});
expect(minio.getObject).toHaveBeenCalledWith(
'blog/live2d/pio/v1/manifest.json',
'kt-template-online',
);
});
it('rejects runtime path traversal before touching MinIO', async () => {
const minio = createMinio();
const service = new BlogLive2DAssetService(