mirror of
https://github.com/KwiTsukasa/kt-template-online-api.git
synced 2026-05-27 15:44:54 +08:00
feat: 增加截图资源代理接口
This commit is contained in:
parent
8c0ca0f0f1
commit
6664deb3cf
@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
BadRequestException,
|
||||||
Controller,
|
Controller,
|
||||||
Body,
|
Body,
|
||||||
Delete,
|
Delete,
|
||||||
@ -34,6 +35,18 @@ import {
|
|||||||
MinioUploadResultDto,
|
MinioUploadResultDto,
|
||||||
} from './minio.dto';
|
} from './minio.dto';
|
||||||
|
|
||||||
|
const PROXY_RESOURCE_TIMEOUT = 1000 * 15;
|
||||||
|
const PROXY_RESOURCE_CONTENT_TYPES = [
|
||||||
|
'image/',
|
||||||
|
'font/',
|
||||||
|
'text/css',
|
||||||
|
'application/font',
|
||||||
|
'application/x-font',
|
||||||
|
'application/vnd.ms-fontobject',
|
||||||
|
];
|
||||||
|
const PROXY_RESOURCE_EXTENSION_RE =
|
||||||
|
/\.(avif|bmp|css|eot|gif|ico|jpe?g|otf|png|svg|ttf|webp|woff2?)(?:[?#].*)?$/i;
|
||||||
|
|
||||||
@Controller('minio')
|
@Controller('minio')
|
||||||
@ApiTags('minio')
|
@ApiTags('minio')
|
||||||
export class MinioClientController {
|
export class MinioClientController {
|
||||||
@ -172,6 +185,47 @@ export class MinioClientController {
|
|||||||
res.send(this.toolsService.res(HttpStatus.OK, '操作成功', result));
|
res.send(this.toolsService.res(HttpStatus.OK, '操作成功', result));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get('resource-proxy')
|
||||||
|
@ApiOperation({ summary: '代理截图所需的图片/CSS/字体资源' })
|
||||||
|
@ApiQuery({ name: 'url' })
|
||||||
|
async proxyResource(@Res() res: Response, @Query('url') url: string) {
|
||||||
|
const target = this.getProxyResourceUrl(url);
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timer = setTimeout(() => controller.abort(), PROXY_RESOURCE_TIMEOUT);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(target, {
|
||||||
|
redirect: 'follow',
|
||||||
|
signal: controller.signal,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new BadRequestException(`资源代理失败:${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const contentType =
|
||||||
|
response.headers.get('content-type') || 'application/octet-stream';
|
||||||
|
|
||||||
|
if (!this.isAllowedProxyResource(contentType, target)) {
|
||||||
|
throw new BadRequestException('仅支持代理图片、CSS 和字体资源');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = Buffer.from(await response.arrayBuffer());
|
||||||
|
|
||||||
|
res.setHeader('Content-Type', contentType);
|
||||||
|
res.setHeader('Cache-Control', 'public, max-age=300');
|
||||||
|
res.send(data);
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof BadRequestException) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new BadRequestException('资源代理失败');
|
||||||
|
} finally {
|
||||||
|
clearTimeout(timer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Get('download')
|
@Get('download')
|
||||||
@ApiOperation({ summary: '下载MinIO文件' })
|
@ApiOperation({ summary: '下载MinIO文件' })
|
||||||
@ApiQuery({ name: 'objectName' })
|
@ApiQuery({ name: 'objectName' })
|
||||||
@ -223,4 +277,36 @@ export class MinioClientController {
|
|||||||
|
|
||||||
res.send(this.toolsService.res(HttpStatus.OK, '操作成功', result));
|
res.send(this.toolsService.res(HttpStatus.OK, '操作成功', result));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getProxyResourceUrl(url: string) {
|
||||||
|
if (!url) {
|
||||||
|
throw new BadRequestException('资源地址不能为空');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const target = new URL(url);
|
||||||
|
|
||||||
|
if (!['http:', 'https:'].includes(target.protocol)) {
|
||||||
|
throw new BadRequestException('仅支持 http/https 资源');
|
||||||
|
}
|
||||||
|
|
||||||
|
return target.toString();
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof BadRequestException) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new BadRequestException('资源地址不合法');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private isAllowedProxyResource(contentType: string, target: string) {
|
||||||
|
const normalizedType = contentType.split(';')[0].trim().toLowerCase();
|
||||||
|
|
||||||
|
return (
|
||||||
|
PROXY_RESOURCE_CONTENT_TYPES.some((type) =>
|
||||||
|
normalizedType.startsWith(type),
|
||||||
|
) || PROXY_RESOURCE_EXTENSION_RE.test(target)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user