feat(api): idempotent upload session init with presigned credential
POST /v1/uploads (Task 18, A03): @@unique(userId, clientTaskId) idempotency anchor — same params return the original session (201, no duplicate photo); param drift (album/bytes/contentType/extension) -> 409 UPLOAD_SESSION_CONFLICT. photos.objectKey is set to the FINAL originals key at init while the session holds the staging key; IDs come from an app-side cuid-like generator so both self-referencing keys are written in one transaction. Credential is a real MinIO presigned PUT (SIGNED_URL_TTL_SECONDS, lazily read). Permission order: album invisible 404 -> member 403 -> uploadEnabled 409 -> size/ext 400 -> category ownership 400. No audit on init (audit anchors on complete). Pre-step for Task 19: QUEUE_PHOTO_PROCESS contract constant + lazy-safe BullMQ queue producer (Redis down at boot must not crash the app; enqueue failure only logs — sweep compensates). e2e reset now also wipes the MinIO photo-live/dev prefix; jest maps @photo/storage to TS source. Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
// services/api/src/uploads/uploads.controller.ts —— /uploads 端点(Task 18 起)。
|
||||
// 方法名与契约 operationId 一一对应(initUpload;Task 19 增补 renewUploadCredential/
|
||||
// completeUpload/listUploads)。控制器级 SessionGuard:端点全需认证。职责:
|
||||
// 透传认证用户 + 查询参数规整(parseLimit/parseCursor 复用相册控制器导出)。
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Post,
|
||||
Req,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { userOf } from '../albums/albums.controller';
|
||||
import { AuthenticatedRequest, SessionGuard } from '../auth/session.guard';
|
||||
import { UploadsService } from './uploads.service';
|
||||
import type { UploadInitResponse } from './uploads.service';
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user