2026-09-17 23:00:19 +08:00
|
|
|
|
// services/api/src/uploads/uploads.controller.ts —— /uploads 端点(Task 18 起;
|
|
|
|
|
|
// Task 19 增补 listUploads/renewUploadCredential/completeUpload)。方法名与契约
|
|
|
|
|
|
// operationId 一一对应。控制器级 SessionGuard:端点全需认证。职责:透传认证
|
|
|
|
|
|
// 用户 + 查询参数规整(parseLimit/parseCursor 复用相册控制器导出,同契约 bounds)。
|
|
|
|
|
|
// 注意 DTO 必须值导入(非 import type):@Body 参数类型经 emitDecoratorMetadata
|
|
|
|
|
|
// 进入设计期元数据,import type 会令参数在管线中被置为 undefined(Task 18 实测)。
|
2026-09-17 22:42:13 +08:00
|
|
|
|
import {
|
|
|
|
|
|
Body,
|
|
|
|
|
|
Controller,
|
2026-09-17 23:00:19 +08:00
|
|
|
|
Get,
|
|
|
|
|
|
HttpCode,
|
|
|
|
|
|
HttpStatus,
|
|
|
|
|
|
Param,
|
2026-09-17 22:42:13 +08:00
|
|
|
|
Post,
|
2026-09-17 23:00:19 +08:00
|
|
|
|
Query,
|
2026-09-17 22:42:13 +08:00
|
|
|
|
Req,
|
|
|
|
|
|
UseGuards,
|
|
|
|
|
|
} from '@nestjs/common';
|
2026-09-17 23:00:19 +08:00
|
|
|
|
import {
|
|
|
|
|
|
parseCursor,
|
|
|
|
|
|
parseLimit,
|
|
|
|
|
|
userOf,
|
|
|
|
|
|
} from '../albums/albums.controller';
|
2026-09-17 22:42:13 +08:00
|
|
|
|
import { AuthenticatedRequest, SessionGuard } from '../auth/session.guard';
|
|
|
|
|
|
import { UploadsService } from './uploads.service';
|
2026-09-17 23:00:19 +08:00
|
|
|
|
import type {
|
|
|
|
|
|
UploadCompleteResponse,
|
|
|
|
|
|
UploadInitResponse,
|
|
|
|
|
|
UploadSessionListResponse,
|
|
|
|
|
|
} from './uploads.service';
|
2026-09-17 22:42:13 +08:00
|
|
|
|
import { UploadInitRequestDto } from './dto';
|
|
|
|
|
|
|
|
|
|
|
|
@Controller('uploads')
|
|
|
|
|
|
@UseGuards(SessionGuard)
|
|
|
|
|
|
export class UploadsController {
|
|
|
|
|
|
constructor(private readonly uploadsService: UploadsService) {}
|
|
|
|
|
|
|
|
|
|
|
|
/** 契约 201(幂等重复调用返回原会话,同 201)。 */
|
|
|
|
|
|
@Post()
|
|
|
|
|
|
initUpload(
|
|
|
|
|
|
@Body() dto: UploadInitRequestDto,
|
|
|
|
|
|
@Req() req: AuthenticatedRequest,
|
|
|
|
|
|
): Promise<UploadInitResponse> {
|
|
|
|
|
|
return this.uploadsService.init(userOf(req), dto);
|
|
|
|
|
|
}
|
2026-09-17 23:00:19 +08:00
|
|
|
|
|
|
|
|
|
|
/** 上传历史:当前用户自己的会话,新在前,(createdAt,id) 键集游标。 */
|
|
|
|
|
|
@Get()
|
|
|
|
|
|
listUploads(
|
|
|
|
|
|
@Query('cursor') cursor: string | undefined,
|
|
|
|
|
|
@Query('limit') limit: string | undefined,
|
|
|
|
|
|
@Req() req: AuthenticatedRequest,
|
|
|
|
|
|
): Promise<UploadSessionListResponse> {
|
|
|
|
|
|
return this.uploadsService.list(
|
|
|
|
|
|
userOf(req),
|
|
|
|
|
|
parseCursor(cursor),
|
|
|
|
|
|
parseLimit(limit),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 续签:仅 pending 会话;返回结构同 initUpload(credential 为新值)。 */
|
|
|
|
|
|
@Post(':uploadId/renew')
|
|
|
|
|
|
@HttpCode(HttpStatus.OK)
|
|
|
|
|
|
renewUploadCredential(
|
|
|
|
|
|
@Param('uploadId') uploadId: string,
|
|
|
|
|
|
@Req() req: AuthenticatedRequest,
|
|
|
|
|
|
): Promise<UploadInitResponse> {
|
|
|
|
|
|
return this.uploadsService.renew(userOf(req), uploadId);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 完成核验:幂等(重复调用返回相同结果,不重复建 job)。 */
|
|
|
|
|
|
@Post(':uploadId/complete')
|
|
|
|
|
|
@HttpCode(HttpStatus.OK)
|
|
|
|
|
|
completeUpload(
|
|
|
|
|
|
@Param('uploadId') uploadId: string,
|
|
|
|
|
|
@Req() req: AuthenticatedRequest,
|
|
|
|
|
|
): Promise<UploadCompleteResponse> {
|
|
|
|
|
|
return this.uploadsService.complete(userOf(req), uploadId);
|
|
|
|
|
|
}
|
2026-09-17 22:42:13 +08:00
|
|
|
|
}
|