Files
photo-live/services/api/src/albums/albums.controller.ts
T

102 lines
3.0 KiB
TypeScript
Raw Normal View History

// services/api/src/albums/albums.controller.ts —— /albums 端点(Task 15;Task 16
// 增补 updateAlbum)。方法名与契约 operationId 一一对应(createAlbum/
// listAlbums/getAlbum/updateAlbum)。控制器级 SessionGuard:四端点全需认证
// (契约均有 bearerAuth、无 security: [] 例外)。职责仅剩:
// · 透传认证用户(req.user,守卫挂载);
2026-09-18 08:18:30 +08:00
// · 查询参数规整(limit 1..100 整数、cursor 长度)经 shared/pagination
// (P1a 终审修缮③迁出本控制器),非法 → 400 VALIDATION_FAILED;
// · 权限/可见性→HTTP 映射交由服务层抛 ApiException(404 ALBUM_NOT_FOUND)。
import {
Body,
Controller,
Delete,
Get,
HttpCode,
HttpStatus,
Param,
Patch,
Post,
Query,
Req,
UseGuards,
} from '@nestjs/common';
import { ErrorCodes } from '@photo/contracts';
import { ApiException } from '../api-exception';
import type { AuthUser } from '../auth/session.guard';
import { AuthenticatedRequest, SessionGuard } from '../auth/session.guard';
2026-09-18 08:18:30 +08:00
import { parseCursor, parseLimit } from '../shared/pagination';
import {
type AlbumListResponse,
type AlbumResponse,
AlbumsService,
} from './albums.service';
import { AlbumCreateRequestDto, AlbumUpdateRequestDto } from './dto';
/**
* 守卫放行后提取 req.user;防御性兜底(守卫未挂载属装配错误,按未认证拒绝)。
2026-09-18 08:18:30 +08:00
* members/categories/photos/uploads 控制器复用同一判定,导出。
*/
export function userOf(req: AuthenticatedRequest): AuthUser {
if (req.user === undefined) {
throw new ApiException(
ErrorCodes.AUTH_TOKEN_EXPIRED,
'Session token is missing, invalid, or expired',
HttpStatus.UNAUTHORIZED,
);
}
return req.user;
}
@Controller('albums')
@UseGuards(SessionGuard)
export class AlbumsController {
constructor(private readonly albumsService: AlbumsService) {}
@Post()
createAlbum(
@Body() dto: AlbumCreateRequestDto,
@Req() req: AuthenticatedRequest,
): Promise<AlbumResponse> {
return this.albumsService.create(userOf(req).id, dto);
}
@Get()
listAlbums(
@Query('cursor') cursor: string | undefined,
@Query('limit') limit: string | undefined,
@Req() req: AuthenticatedRequest,
): Promise<AlbumListResponse> {
return this.albumsService.list(
userOf(req),
parseCursor(cursor),
parseLimit(limit),
);
}
@Get(':albumId')
getAlbum(
@Param('albumId') albumId: string,
@Req() req: AuthenticatedRequest,
): Promise<AlbumResponse> {
return this.albumsService.get(userOf(req), albumId);
}
@Delete(':albumId')
@HttpCode(HttpStatus.NO_CONTENT)
deleteAlbum(
@Param('albumId') albumId: string,
@Req() req: AuthenticatedRequest,
): Promise<void> {
return this.albumsService.softDelete(userOf(req), albumId);
}
@Patch(':albumId')
updateAlbum(
@Param('albumId') albumId: string,
@Body() dto: AlbumUpdateRequestDto,
@Req() req: AuthenticatedRequest,
): Promise<AlbumResponse> {
return this.albumsService.update(userOf(req), albumId, dto);
}
}