fix(server): thumbnail content type not being passed to stream handle (#3137)

* asset mimetype instead of application/octet-stream

* use thumbnail mimetype instead

* narrowed openapi spec

* thumbnail format validation

* JPEG fallback, `getThumbnailPath` returns format

* return content type in `getThumbnailPath`

* moved `format` validation to dto

* removed unused import

* moved fallback warning

* added `ApiOkResponse`
This commit is contained in:
Mert 2023-07-08 16:07:56 -04:00 committed by GitHub
parent d590dec159
commit a5cc408469
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 35 additions and 14 deletions

View file

@ -822,7 +822,7 @@ Name | Type | Description | Notes
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/octet-stream
- **Accept**: image/jpeg, image/webp
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)

View file

@ -228,7 +228,7 @@ Name | Type | Description | Notes
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/octet-stream
- **Accept**: image/jpeg
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)

View file

@ -1673,7 +1673,13 @@
"responses": {
"200": {
"content": {
"application/octet-stream": {
"image/jpeg": {
"schema": {
"type": "string",
"format": "binary"
}
},
"image/webp": {
"schema": {
"type": "string",
"format": "binary"
@ -2704,7 +2710,7 @@
"responses": {
"200": {
"content": {
"application/octet-stream": {
"image/jpeg": {
"schema": {
"type": "string",
"format": "binary"

View file

@ -122,7 +122,11 @@ export class AssetController {
@SharedLinkRoute()
@Get('/file/:id')
@Header('Cache-Control', 'private, max-age=86400, no-transform')
@ApiOkResponse({ content: { 'application/octet-stream': { schema: { type: 'string', format: 'binary' } } } })
@ApiOkResponse({
content: {
'application/octet-stream': { schema: { type: 'string', format: 'binary' } },
},
})
serveFile(
@AuthUser() authUser: AuthUserDto,
@Headers() headers: Record<string, string>,
@ -136,7 +140,12 @@ export class AssetController {
@SharedLinkRoute()
@Get('/thumbnail/:id')
@Header('Cache-Control', 'private, max-age=86400, no-transform')
@ApiOkResponse({ content: { 'application/octet-stream': { schema: { type: 'string', format: 'binary' } } } })
@ApiOkResponse({
content: {
'image/jpeg': { schema: { type: 'string', format: 'binary' } },
'image/webp': { schema: { type: 'string', format: 'binary' } },
},
})
getAssetThumbnail(
@AuthUser() authUser: AuthUserDto,
@Headers() headers: Record<string, string>,

View file

@ -256,8 +256,8 @@ export class AssetService {
}
try {
const thumbnailPath = this.getThumbnailPath(asset, query.format);
return this.streamFile(thumbnailPath, res, headers);
const [thumbnailPath, contentType] = this.getThumbnailPath(asset, query.format);
return this.streamFile(thumbnailPath, res, headers, contentType);
} catch (e) {
res.header('Cache-Control', 'none');
this.logger.error(`Cannot create read stream for asset ${asset.id}`, 'getAssetThumbnail');
@ -522,16 +522,17 @@ export class AssetService {
private getThumbnailPath(asset: AssetEntity, format: GetAssetThumbnailFormatEnum) {
switch (format) {
case GetAssetThumbnailFormatEnum.WEBP:
if (asset.webpPath && asset.webpPath.length > 0) {
return asset.webpPath;
if (asset.webpPath) {
return [asset.webpPath, 'image/webp'];
}
this.logger.warn(`WebP thumbnail requested but not found for asset ${asset.id}, falling back to JPEG`);
case GetAssetThumbnailFormatEnum.JPEG:
default:
if (!asset.resizePath) {
throw new NotFoundException('resizePath not set');
throw new NotFoundException(`No thumbnail found for asset ${asset.id}`);
}
return asset.resizePath;
return [asset.resizePath, 'image/jpeg'];
}
}

View file

@ -1,5 +1,5 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsOptional } from 'class-validator';
import { IsEnum, IsOptional } from 'class-validator';
export enum GetAssetThumbnailFormatEnum {
JPEG = 'JPEG',
@ -8,6 +8,7 @@ export enum GetAssetThumbnailFormatEnum {
export class GetAssetThumbnailDto {
@IsOptional()
@IsEnum(GetAssetThumbnailFormatEnum)
@ApiProperty({
type: String,
enum: GetAssetThumbnailFormatEnum,

View file

@ -43,7 +43,11 @@ export class PersonController {
}
@Get(':id/thumbnail')
@ApiOkResponse({ content: { 'application/octet-stream': { schema: { type: 'string', format: 'binary' } } } })
@ApiOkResponse({
content: {
'image/jpeg': { schema: { type: 'string', format: 'binary' } },
},
})
getPersonThumbnail(@AuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto) {
return this.service.getThumbnail(authUser, id).then(asStreamableFile);
}