feat: 完成现有小程序接口的全部迁移至新后端
This commit is contained in:
@@ -4,6 +4,7 @@ import {
|
||||
Get,
|
||||
Param,
|
||||
Patch,
|
||||
Put,
|
||||
Query,
|
||||
Req,
|
||||
} from '@nestjs/common';
|
||||
@@ -59,6 +60,17 @@ export class AdminWorksheetController {
|
||||
};
|
||||
}
|
||||
|
||||
@Put(':id/status')
|
||||
@ApiOperation({ summary: '更新发布状态(管理员,小程序 PUT 兼容入口)' })
|
||||
async updateByPut(
|
||||
@Param('id') id: string,
|
||||
@Body() input: UpdateContentStatusDto,
|
||||
@CurrentUser() actor: AuthenticatedUser,
|
||||
@Req() request: HttpRequest,
|
||||
) {
|
||||
return this.update(id, input, actor, request);
|
||||
}
|
||||
|
||||
@Patch(':id/status')
|
||||
@ApiOperation({
|
||||
summary: '题型发布、下架或转草稿(管理员);发布要求分类已上线',
|
||||
@@ -115,6 +127,17 @@ export class AdminResourceController {
|
||||
return { code: 0, message: 'ok', data: await this.resources.get(id) };
|
||||
}
|
||||
|
||||
@Put(':id/status')
|
||||
@ApiOperation({ summary: '更新发布状态(管理员,小程序 PUT 兼容入口)' })
|
||||
async updateByPut(
|
||||
@Param('id') id: string,
|
||||
@Body() input: UpdateContentStatusDto,
|
||||
@CurrentUser() actor: AuthenticatedUser,
|
||||
@Req() request: HttpRequest,
|
||||
) {
|
||||
return this.update(id, input, actor, request);
|
||||
}
|
||||
|
||||
@Patch(':id/status')
|
||||
@ApiOperation({
|
||||
summary: '资源发布、下架或转草稿(管理员);发布要求题型及分类已上线',
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { publicObjectUrl } from './public-resource-url';
|
||||
|
||||
/** 仅解析已就绪的公开预览图;管理预览同样不绕过资产访问规则。 */
|
||||
export interface PreviewAssetRow {
|
||||
assetStatus: string;
|
||||
@@ -39,15 +41,7 @@ export function previewUrl(
|
||||
}
|
||||
}
|
||||
if (row.assetProvider === 'qiniu' && row.assetKey && qiniuPublicDomain) {
|
||||
const parts = row.assetKey.split('/');
|
||||
if (
|
||||
row.assetKey.startsWith('/') ||
|
||||
row.assetKey.includes('\\') ||
|
||||
/[\r\n]/.test(row.assetKey) ||
|
||||
parts.some((part) => part === '.' || part === '..')
|
||||
)
|
||||
return null;
|
||||
return `${qiniuPublicDomain}/${parts.map((part) => encodeURIComponent(part)).join('/')}`;
|
||||
return publicObjectUrl(qiniuPublicDomain, row.assetKey);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import type { ServerConfig } from '../config/server-config';
|
||||
|
||||
/** 数据库 objectKey 已含上传前缀;逐段编码且拒绝越界,不再次拼接前缀。 */
|
||||
export function publicObjectUrl(
|
||||
domain: string | null,
|
||||
objectKey: string,
|
||||
): string | null {
|
||||
const parts = objectKey.split('/');
|
||||
if (
|
||||
!domain ||
|
||||
objectKey.includes('\\') ||
|
||||
[...objectKey].some(
|
||||
(char) => char.charCodeAt(0) < 32 || char.charCodeAt(0) === 127,
|
||||
) ||
|
||||
parts.some((part) => !part || part === '.' || part === '..')
|
||||
)
|
||||
return null;
|
||||
return `${domain}/${parts.map(encodeURIComponent).join('/')}`;
|
||||
}
|
||||
|
||||
/** 字体等固定公开资源传备份相对路径,由配置统一补上传前缀;不解析旧 cloud 引用。 */
|
||||
export function publicResourceUrl(
|
||||
config: Pick<ServerConfig, 'qiniuPublicDomain' | 'qiniuKeyPrefix'>,
|
||||
relativePath: string,
|
||||
): string | null {
|
||||
// 先独立验证相对路径,防止空路径或绝对路径被前缀掩盖。
|
||||
if (!publicObjectUrl(config.qiniuPublicDomain, relativePath)) return null;
|
||||
return publicObjectUrl(
|
||||
config.qiniuPublicDomain,
|
||||
`${config.qiniuKeyPrefix}${relativePath}`,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user