1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2025-01-04 02:46:47 +01:00

refactor(server): auth decorator (#2588)

This commit is contained in:
Jason Rasmussen 2023-05-28 12:30:01 -04:00 committed by GitHub
parent e7ad622c02
commit ffe397247e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 62 additions and 71 deletions

View file

@ -1,7 +1,7 @@
import { Controller, Get, Post, Body, Param, Delete, Put, Query, Response } from '@nestjs/common'; import { Controller, Get, Post, Body, Param, Delete, Put, Query, Response } from '@nestjs/common';
import { ParseMeUUIDPipe } from '../validation/parse-me-uuid-pipe'; import { ParseMeUUIDPipe } from '../validation/parse-me-uuid-pipe';
import { AlbumService } from './album.service'; import { AlbumService } from './album.service';
import { Authenticated } from '../../decorators/authenticated.decorator'; import { Authenticated, SharedLinkRoute } from '../../decorators/authenticated.decorator';
import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator'; import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
import { AddAssetsDto } from './dto/add-assets.dto'; import { AddAssetsDto } from './dto/add-assets.dto';
import { AddUsersDto } from './dto/add-users.dto'; import { AddUsersDto } from './dto/add-users.dto';
@ -32,24 +32,23 @@ const handleDownload = (download: DownloadArchive, res: Res) => {
@ApiTags('Album') @ApiTags('Album')
@Controller('album') @Controller('album')
@Authenticated()
@UseValidation() @UseValidation()
export class AlbumController { export class AlbumController {
constructor(private readonly service: AlbumService) {} constructor(private readonly service: AlbumService) {}
@Authenticated()
@Get('count-by-user-id') @Get('count-by-user-id')
getAlbumCountByUserId(@GetAuthUser() authUser: AuthUserDto): Promise<AlbumCountResponseDto> { getAlbumCountByUserId(@GetAuthUser() authUser: AuthUserDto): Promise<AlbumCountResponseDto> {
return this.service.getCountByUserId(authUser); return this.service.getCountByUserId(authUser);
} }
@Authenticated()
@Put(':id/users') @Put(':id/users')
addUsersToAlbum(@GetAuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto, @Body() dto: AddUsersDto) { addUsersToAlbum(@GetAuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto, @Body() dto: AddUsersDto) {
// TODO: Handle nonexistent sharedUserIds. // TODO: Handle nonexistent sharedUserIds.
return this.service.addUsers(authUser, id, dto); return this.service.addUsers(authUser, id, dto);
} }
@Authenticated({ isShared: true }) @SharedLinkRoute()
@Put(':id/assets') @Put(':id/assets')
addAssetsToAlbum( addAssetsToAlbum(
@GetAuthUser() authUser: AuthUserDto, @GetAuthUser() authUser: AuthUserDto,
@ -61,13 +60,12 @@ export class AlbumController {
return this.service.addAssets(authUser, id, dto); return this.service.addAssets(authUser, id, dto);
} }
@Authenticated({ isShared: true }) @SharedLinkRoute()
@Get(':id') @Get(':id')
getAlbumInfo(@GetAuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto) { getAlbumInfo(@GetAuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto) {
return this.service.get(authUser, id); return this.service.get(authUser, id);
} }
@Authenticated()
@Delete(':id/assets') @Delete(':id/assets')
removeAssetFromAlbum( removeAssetFromAlbum(
@GetAuthUser() authUser: AuthUserDto, @GetAuthUser() authUser: AuthUserDto,
@ -77,7 +75,6 @@ export class AlbumController {
return this.service.removeAssets(authUser, id, dto); return this.service.removeAssets(authUser, id, dto);
} }
@Authenticated()
@Delete(':id/user/:userId') @Delete(':id/user/:userId')
removeUserFromAlbum( removeUserFromAlbum(
@GetAuthUser() authUser: AuthUserDto, @GetAuthUser() authUser: AuthUserDto,
@ -87,7 +84,7 @@ export class AlbumController {
return this.service.removeUser(authUser, id, userId); return this.service.removeUser(authUser, id, userId);
} }
@Authenticated({ isShared: true }) @SharedLinkRoute()
@Get(':id/download') @Get(':id/download')
@ApiOkResponse({ content: { 'application/zip': { schema: { type: 'string', format: 'binary' } } } }) @ApiOkResponse({ content: { 'application/zip': { schema: { type: 'string', format: 'binary' } } } })
downloadArchive( downloadArchive(
@ -100,7 +97,6 @@ export class AlbumController {
return this.service.downloadArchive(authUser, id, dto).then((download) => handleDownload(download, res)); return this.service.downloadArchive(authUser, id, dto).then((download) => handleDownload(download, res));
} }
@Authenticated()
@Post('create-shared-link') @Post('create-shared-link')
createAlbumSharedLink(@GetAuthUser() authUser: AuthUserDto, @Body() dto: CreateAlbumSharedLinkDto) { createAlbumSharedLink(@GetAuthUser() authUser: AuthUserDto, @Body() dto: CreateAlbumSharedLinkDto) {
return this.service.createSharedLink(authUser, dto); return this.service.createSharedLink(authUser, dto);

View file

@ -19,7 +19,7 @@ import {
StreamableFile, StreamableFile,
ParseFilePipe, ParseFilePipe,
} from '@nestjs/common'; } from '@nestjs/common';
import { Authenticated } from '../../decorators/authenticated.decorator'; import { Authenticated, SharedLinkRoute } from '../../decorators/authenticated.decorator';
import { AssetService } from './asset.service'; import { AssetService } from './asset.service';
import { FileFieldsInterceptor } from '@nestjs/platform-express'; import { FileFieldsInterceptor } from '@nestjs/platform-express';
import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator'; import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
@ -68,10 +68,11 @@ function asStreamableFile({ stream, type, length }: ImmichReadStream) {
@ApiTags('Asset') @ApiTags('Asset')
@Controller('asset') @Controller('asset')
@Authenticated()
export class AssetController { export class AssetController {
constructor(private assetService: AssetService) {} constructor(private assetService: AssetService) {}
@Authenticated({ isShared: true }) @SharedLinkRoute()
@Post('upload') @Post('upload')
@UseInterceptors( @UseInterceptors(
FileFieldsInterceptor( FileFieldsInterceptor(
@ -116,7 +117,7 @@ export class AssetController {
return responseDto; return responseDto;
} }
@Authenticated({ isShared: true }) @SharedLinkRoute()
@Get('/download/:assetId') @Get('/download/:assetId')
@ApiOkResponse({ content: { 'application/octet-stream': { schema: { type: 'string', format: 'binary' } } } }) @ApiOkResponse({ content: { 'application/octet-stream': { schema: { type: 'string', format: 'binary' } } } })
async downloadFile( async downloadFile(
@ -127,7 +128,7 @@ export class AssetController {
return this.assetService.downloadFile(authUser, assetId).then(asStreamableFile); return this.assetService.downloadFile(authUser, assetId).then(asStreamableFile);
} }
@Authenticated({ isShared: true }) @SharedLinkRoute()
@Post('/download-files') @Post('/download-files')
@ApiOkResponse({ content: { 'application/octet-stream': { schema: { type: 'string', format: 'binary' } } } }) @ApiOkResponse({ content: { 'application/octet-stream': { schema: { type: 'string', format: 'binary' } } } })
async downloadFiles( async downloadFiles(
@ -148,7 +149,7 @@ export class AssetController {
/** /**
* Current this is not used in any UI element * Current this is not used in any UI element
*/ */
@Authenticated({ isShared: true }) @SharedLinkRoute()
@Get('/download-library') @Get('/download-library')
@ApiOkResponse({ content: { 'application/octet-stream': { schema: { type: 'string', format: 'binary' } } } }) @ApiOkResponse({ content: { 'application/octet-stream': { schema: { type: 'string', format: 'binary' } } } })
async downloadLibrary( async downloadLibrary(
@ -165,7 +166,7 @@ export class AssetController {
return stream; return stream;
} }
@Authenticated({ isShared: true }) @SharedLinkRoute()
@Get('/file/:assetId') @Get('/file/:assetId')
@Header('Cache-Control', 'max-age=31536000') @Header('Cache-Control', 'max-age=31536000')
@ApiOkResponse({ content: { 'application/octet-stream': { schema: { type: 'string', format: 'binary' } } } }) @ApiOkResponse({ content: { 'application/octet-stream': { schema: { type: 'string', format: 'binary' } } } })
@ -180,7 +181,7 @@ export class AssetController {
return this.assetService.serveFile(authUser, assetId, query, res, headers); return this.assetService.serveFile(authUser, assetId, query, res, headers);
} }
@Authenticated({ isShared: true }) @SharedLinkRoute()
@Get('/thumbnail/:assetId') @Get('/thumbnail/:assetId')
@Header('Cache-Control', 'max-age=31536000') @Header('Cache-Control', 'max-age=31536000')
@ApiOkResponse({ content: { 'application/octet-stream': { schema: { type: 'string', format: 'binary' } } } }) @ApiOkResponse({ content: { 'application/octet-stream': { schema: { type: 'string', format: 'binary' } } } })
@ -195,25 +196,21 @@ export class AssetController {
return this.assetService.getAssetThumbnail(assetId, query, res, headers); return this.assetService.getAssetThumbnail(assetId, query, res, headers);
} }
@Authenticated()
@Get('/curated-objects') @Get('/curated-objects')
async getCuratedObjects(@GetAuthUser() authUser: AuthUserDto): Promise<CuratedObjectsResponseDto[]> { async getCuratedObjects(@GetAuthUser() authUser: AuthUserDto): Promise<CuratedObjectsResponseDto[]> {
return this.assetService.getCuratedObject(authUser); return this.assetService.getCuratedObject(authUser);
} }
@Authenticated()
@Get('/curated-locations') @Get('/curated-locations')
async getCuratedLocations(@GetAuthUser() authUser: AuthUserDto): Promise<CuratedLocationsResponseDto[]> { async getCuratedLocations(@GetAuthUser() authUser: AuthUserDto): Promise<CuratedLocationsResponseDto[]> {
return this.assetService.getCuratedLocation(authUser); return this.assetService.getCuratedLocation(authUser);
} }
@Authenticated()
@Get('/search-terms') @Get('/search-terms')
async getAssetSearchTerms(@GetAuthUser() authUser: AuthUserDto): Promise<string[]> { async getAssetSearchTerms(@GetAuthUser() authUser: AuthUserDto): Promise<string[]> {
return this.assetService.getAssetSearchTerm(authUser); return this.assetService.getAssetSearchTerm(authUser);
} }
@Authenticated()
@Post('/search') @Post('/search')
async searchAsset( async searchAsset(
@GetAuthUser() authUser: AuthUserDto, @GetAuthUser() authUser: AuthUserDto,
@ -222,7 +219,6 @@ export class AssetController {
return this.assetService.searchAsset(authUser, searchAssetDto); return this.assetService.searchAsset(authUser, searchAssetDto);
} }
@Authenticated()
@Post('/count-by-time-bucket') @Post('/count-by-time-bucket')
async getAssetCountByTimeBucket( async getAssetCountByTimeBucket(
@GetAuthUser() authUser: AuthUserDto, @GetAuthUser() authUser: AuthUserDto,
@ -231,13 +227,11 @@ export class AssetController {
return this.assetService.getAssetCountByTimeBucket(authUser, getAssetCountByTimeGroupDto); return this.assetService.getAssetCountByTimeBucket(authUser, getAssetCountByTimeGroupDto);
} }
@Authenticated()
@Get('/count-by-user-id') @Get('/count-by-user-id')
async getAssetCountByUserId(@GetAuthUser() authUser: AuthUserDto): Promise<AssetCountByUserIdResponseDto> { async getAssetCountByUserId(@GetAuthUser() authUser: AuthUserDto): Promise<AssetCountByUserIdResponseDto> {
return this.assetService.getAssetCountByUserId(authUser); return this.assetService.getAssetCountByUserId(authUser);
} }
@Authenticated()
@Get('/stat/archive') @Get('/stat/archive')
async getArchivedAssetCountByUserId(@GetAuthUser() authUser: AuthUserDto): Promise<AssetCountByUserIdResponseDto> { async getArchivedAssetCountByUserId(@GetAuthUser() authUser: AuthUserDto): Promise<AssetCountByUserIdResponseDto> {
return this.assetService.getArchivedAssetCountByUserId(authUser); return this.assetService.getArchivedAssetCountByUserId(authUser);
@ -245,7 +239,6 @@ export class AssetController {
/** /**
* Get all AssetEntity belong to the user * Get all AssetEntity belong to the user
*/ */
@Authenticated()
@Get('/') @Get('/')
@ApiHeader({ @ApiHeader({
name: 'if-none-match', name: 'if-none-match',
@ -260,7 +253,6 @@ export class AssetController {
return this.assetService.getAllAssets(authUser, dto); return this.assetService.getAllAssets(authUser, dto);
} }
@Authenticated()
@Post('/time-bucket') @Post('/time-bucket')
async getAssetByTimeBucket( async getAssetByTimeBucket(
@GetAuthUser() authUser: AuthUserDto, @GetAuthUser() authUser: AuthUserDto,
@ -272,7 +264,6 @@ export class AssetController {
/** /**
* Get all asset of a device that are in the database, ID only. * Get all asset of a device that are in the database, ID only.
*/ */
@Authenticated()
@Get('/:deviceId') @Get('/:deviceId')
async getUserAssetsByDeviceId(@GetAuthUser() authUser: AuthUserDto, @Param() { deviceId }: DeviceIdDto) { async getUserAssetsByDeviceId(@GetAuthUser() authUser: AuthUserDto, @Param() { deviceId }: DeviceIdDto) {
return await this.assetService.getUserAssetsByDeviceId(authUser, deviceId); return await this.assetService.getUserAssetsByDeviceId(authUser, deviceId);
@ -281,7 +272,7 @@ export class AssetController {
/** /**
* Get a single asset's information * Get a single asset's information
*/ */
@Authenticated({ isShared: true }) @SharedLinkRoute()
@Get('/assetById/:assetId') @Get('/assetById/:assetId')
async getAssetById( async getAssetById(
@GetAuthUser() authUser: AuthUserDto, @GetAuthUser() authUser: AuthUserDto,
@ -294,7 +285,6 @@ export class AssetController {
/** /**
* Update an asset * Update an asset
*/ */
@Authenticated()
@Put('/:assetId') @Put('/:assetId')
async updateAsset( async updateAsset(
@GetAuthUser() authUser: AuthUserDto, @GetAuthUser() authUser: AuthUserDto,
@ -305,7 +295,6 @@ export class AssetController {
return await this.assetService.updateAsset(authUser, assetId, dto); return await this.assetService.updateAsset(authUser, assetId, dto);
} }
@Authenticated()
@Delete('/') @Delete('/')
async deleteAsset( async deleteAsset(
@GetAuthUser() authUser: AuthUserDto, @GetAuthUser() authUser: AuthUserDto,
@ -318,7 +307,7 @@ export class AssetController {
/** /**
* Check duplicated asset before uploading - for Web upload used * Check duplicated asset before uploading - for Web upload used
*/ */
@Authenticated({ isShared: true }) @SharedLinkRoute()
@Post('/check') @Post('/check')
@HttpCode(200) @HttpCode(200)
async checkDuplicateAsset( async checkDuplicateAsset(
@ -331,7 +320,6 @@ export class AssetController {
/** /**
* Checks if multiple assets exist on the server and returns all existing - used by background backup * Checks if multiple assets exist on the server and returns all existing - used by background backup
*/ */
@Authenticated()
@Post('/exist') @Post('/exist')
@HttpCode(200) @HttpCode(200)
async checkExistingAssets( async checkExistingAssets(
@ -344,7 +332,6 @@ export class AssetController {
/** /**
* Checks if assets exist by checksums * Checks if assets exist by checksums
*/ */
@Authenticated()
@Post('/bulk-upload-check') @Post('/bulk-upload-check')
@HttpCode(200) @HttpCode(200)
bulkUploadCheck( bulkUploadCheck(
@ -354,7 +341,6 @@ export class AssetController {
return this.assetService.bulkUploadCheck(authUser, dto); return this.assetService.bulkUploadCheck(authUser, dto);
} }
@Authenticated()
@Post('/shared-link') @Post('/shared-link')
async createAssetsSharedLink( async createAssetsSharedLink(
@GetAuthUser() authUser: AuthUserDto, @GetAuthUser() authUser: AuthUserDto,
@ -363,7 +349,7 @@ export class AssetController {
return await this.assetService.createAssetsSharedLink(authUser, dto); return await this.assetService.createAssetsSharedLink(authUser, dto);
} }
@Authenticated({ isShared: true }) @SharedLinkRoute()
@Patch('/shared-link/add') @Patch('/shared-link/add')
async addAssetsToSharedLink( async addAssetsToSharedLink(
@GetAuthUser() authUser: AuthUserDto, @GetAuthUser() authUser: AuthUserDto,
@ -372,7 +358,7 @@ export class AssetController {
return await this.assetService.addAssetsToSharedLink(authUser, dto); return await this.assetService.addAssetsToSharedLink(authUser, dto);
} }
@Authenticated({ isShared: true }) @SharedLinkRoute()
@Patch('/shared-link/remove') @Patch('/shared-link/remove')
async removeAssetsFromSharedLink( async removeAssetsFromSharedLink(
@GetAuthUser() authUser: AuthUserDto, @GetAuthUser() authUser: AuthUserDto,

View file

@ -8,9 +8,9 @@ import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
import { mapTag, TagResponseDto } from '@app/domain'; import { mapTag, TagResponseDto } from '@app/domain';
import { UUIDParamDto } from '../../controllers/dto/uuid-param.dto'; import { UUIDParamDto } from '../../controllers/dto/uuid-param.dto';
@Authenticated()
@ApiTags('Tag') @ApiTags('Tag')
@Controller('tag') @Controller('tag')
@Authenticated()
export class TagController { export class TagController {
constructor(private readonly tagService: TagService) {} constructor(private readonly tagService: TagService) {}

View file

@ -19,16 +19,18 @@ import { Body, Controller, Delete, Get, Param, Post, Req, Res } from '@nestjs/co
import { ApiBadRequestResponse, ApiTags } from '@nestjs/swagger'; import { ApiBadRequestResponse, ApiTags } from '@nestjs/swagger';
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import { GetAuthUser, GetLoginDetails } from '../decorators/auth-user.decorator'; import { GetAuthUser, GetLoginDetails } from '../decorators/auth-user.decorator';
import { Authenticated } from '../decorators/authenticated.decorator'; import { Authenticated, PublicRoute } from '../decorators/authenticated.decorator';
import { UseValidation } from '../decorators/use-validation.decorator'; import { UseValidation } from '../decorators/use-validation.decorator';
import { UUIDParamDto } from './dto/uuid-param.dto'; import { UUIDParamDto } from './dto/uuid-param.dto';
@ApiTags('Authentication') @ApiTags('Authentication')
@Controller('auth') @Controller('auth')
@Authenticated()
@UseValidation() @UseValidation()
export class AuthController { export class AuthController {
constructor(private readonly service: AuthService) {} constructor(private readonly service: AuthService) {}
@PublicRoute()
@Post('login') @Post('login')
async login( async login(
@Body() loginCredential: LoginCredentialDto, @Body() loginCredential: LoginCredentialDto,
@ -40,43 +42,38 @@ export class AuthController {
return response; return response;
} }
@PublicRoute()
@Post('admin-sign-up') @Post('admin-sign-up')
@ApiBadRequestResponse({ description: 'The server already has an admin' }) @ApiBadRequestResponse({ description: 'The server already has an admin' })
adminSignUp(@Body() signUpCredential: SignUpDto): Promise<AdminSignupResponseDto> { adminSignUp(@Body() signUpCredential: SignUpDto): Promise<AdminSignupResponseDto> {
return this.service.adminSignUp(signUpCredential); return this.service.adminSignUp(signUpCredential);
} }
@Authenticated()
@Get('devices') @Get('devices')
getAuthDevices(@GetAuthUser() authUser: AuthUserDto): Promise<AuthDeviceResponseDto[]> { getAuthDevices(@GetAuthUser() authUser: AuthUserDto): Promise<AuthDeviceResponseDto[]> {
return this.service.getDevices(authUser); return this.service.getDevices(authUser);
} }
@Authenticated()
@Delete('devices') @Delete('devices')
logoutAuthDevices(@GetAuthUser() authUser: AuthUserDto): Promise<void> { logoutAuthDevices(@GetAuthUser() authUser: AuthUserDto): Promise<void> {
return this.service.logoutDevices(authUser); return this.service.logoutDevices(authUser);
} }
@Authenticated()
@Delete('devices/:id') @Delete('devices/:id')
logoutAuthDevice(@GetAuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<void> { logoutAuthDevice(@GetAuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<void> {
return this.service.logoutDevice(authUser, id); return this.service.logoutDevice(authUser, id);
} }
@Authenticated()
@Post('validateToken') @Post('validateToken')
validateAccessToken(): ValidateAccessTokenResponseDto { validateAccessToken(): ValidateAccessTokenResponseDto {
return { authStatus: true }; return { authStatus: true };
} }
@Authenticated()
@Post('change-password') @Post('change-password')
changePassword(@GetAuthUser() authUser: AuthUserDto, @Body() dto: ChangePasswordDto): Promise<UserResponseDto> { changePassword(@GetAuthUser() authUser: AuthUserDto, @Body() dto: ChangePasswordDto): Promise<UserResponseDto> {
return this.service.changePassword(authUser, dto); return this.service.changePassword(authUser, dto);
} }
@Authenticated()
@Post('logout') @Post('logout')
logout( logout(
@Req() req: Request, @Req() req: Request,

View file

@ -12,15 +12,17 @@ import { Body, Controller, Get, HttpStatus, Post, Redirect, Req, Res } from '@ne
import { ApiTags } from '@nestjs/swagger'; import { ApiTags } from '@nestjs/swagger';
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import { GetAuthUser, GetLoginDetails } from '../decorators/auth-user.decorator'; import { GetAuthUser, GetLoginDetails } from '../decorators/auth-user.decorator';
import { Authenticated } from '../decorators/authenticated.decorator'; import { Authenticated, PublicRoute } from '../decorators/authenticated.decorator';
import { UseValidation } from '../decorators/use-validation.decorator'; import { UseValidation } from '../decorators/use-validation.decorator';
@ApiTags('OAuth') @ApiTags('OAuth')
@Controller('oauth') @Controller('oauth')
@Authenticated()
@UseValidation() @UseValidation()
export class OAuthController { export class OAuthController {
constructor(private service: OAuthService) {} constructor(private service: OAuthService) {}
@PublicRoute()
@Get('mobile-redirect') @Get('mobile-redirect')
@Redirect() @Redirect()
mobileRedirect(@Req() req: Request) { mobileRedirect(@Req() req: Request) {
@ -30,11 +32,13 @@ export class OAuthController {
}; };
} }
@PublicRoute()
@Post('config') @Post('config')
generateConfig(@Body() dto: OAuthConfigDto): Promise<OAuthConfigResponseDto> { generateConfig(@Body() dto: OAuthConfigDto): Promise<OAuthConfigResponseDto> {
return this.service.generateConfig(dto); return this.service.generateConfig(dto);
} }
@PublicRoute()
@Post('callback') @Post('callback')
async callback( async callback(
@Res({ passthrough: true }) res: Response, @Res({ passthrough: true }) res: Response,
@ -46,13 +50,11 @@ export class OAuthController {
return response; return response;
} }
@Authenticated()
@Post('link') @Post('link')
link(@GetAuthUser() authUser: AuthUserDto, @Body() dto: OAuthCallbackDto): Promise<UserResponseDto> { link(@GetAuthUser() authUser: AuthUserDto, @Body() dto: OAuthCallbackDto): Promise<UserResponseDto> {
return this.service.link(authUser, dto); return this.service.link(authUser, dto);
} }
@Authenticated()
@Post('unlink') @Post('unlink')
unlink(@GetAuthUser() authUser: AuthUserDto): Promise<UserResponseDto> { unlink(@GetAuthUser() authUser: AuthUserDto): Promise<UserResponseDto> {
return this.service.unlink(authUser); return this.service.unlink(authUser);

View file

@ -8,11 +8,11 @@ import { UUIDParamDto } from './dto/uuid-param.dto';
@ApiTags('Partner') @ApiTags('Partner')
@Controller('partner') @Controller('partner')
@Authenticated()
@UseValidation() @UseValidation()
export class PartnerController { export class PartnerController {
constructor(private service: PartnerService) {} constructor(private service: PartnerService) {}
@Authenticated()
@Get() @Get()
@ApiQuery({ name: 'direction', type: 'string', enum: PartnerDirection, required: true }) @ApiQuery({ name: 'direction', type: 'string', enum: PartnerDirection, required: true })
getPartners( getPartners(
@ -22,13 +22,11 @@ export class PartnerController {
return this.service.getAll(authUser, direction); return this.service.getAll(authUser, direction);
} }
@Authenticated()
@Post(':id') @Post(':id')
createPartner(@GetAuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<UserResponseDto> { createPartner(@GetAuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<UserResponseDto> {
return this.service.create(authUser, id); return this.service.create(authUser, id);
} }
@Authenticated()
@Delete(':id') @Delete(':id')
removePartner(@GetAuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<void> { removePartner(@GetAuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<void> {
return this.service.remove(authUser, id); return this.service.remove(authUser, id);

View file

@ -7,32 +7,34 @@ import {
} from '@app/domain'; } from '@app/domain';
import { Controller, Get } from '@nestjs/common'; import { Controller, Get } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger'; import { ApiTags } from '@nestjs/swagger';
import { Authenticated } from '../decorators/authenticated.decorator'; import { AdminRoute, Authenticated, PublicRoute } from '../decorators/authenticated.decorator';
import { UseValidation } from '../decorators/use-validation.decorator'; import { UseValidation } from '../decorators/use-validation.decorator';
@ApiTags('Server Info') @ApiTags('Server Info')
@Controller('server-info') @Controller('server-info')
@Authenticated()
@UseValidation() @UseValidation()
export class ServerInfoController { export class ServerInfoController {
constructor(private service: ServerInfoService) {} constructor(private service: ServerInfoService) {}
@Authenticated()
@Get() @Get()
getServerInfo(): Promise<ServerInfoResponseDto> { getServerInfo(): Promise<ServerInfoResponseDto> {
return this.service.getInfo(); return this.service.getInfo();
} }
@PublicRoute()
@Get('/ping') @Get('/ping')
pingServer(): ServerPingResponse { pingServer(): ServerPingResponse {
return this.service.ping(); return this.service.ping();
} }
@PublicRoute()
@Get('/version') @Get('/version')
getServerVersion(): ServerVersionReponseDto { getServerVersion(): ServerVersionReponseDto {
return this.service.getVersion(); return this.service.getVersion();
} }
@Authenticated({ admin: true }) @AdminRoute()
@Get('/stats') @Get('/stats')
getStats(): Promise<ServerStatsResponseDto> { getStats(): Promise<ServerStatsResponseDto> {
return this.service.getStats(); return this.service.getStats();

View file

@ -2,29 +2,28 @@ import { AuthUserDto, EditSharedLinkDto, SharedLinkResponseDto, ShareService } f
import { Body, Controller, Delete, Get, Param, Patch } from '@nestjs/common'; import { Body, Controller, Delete, Get, Param, Patch } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger'; import { ApiTags } from '@nestjs/swagger';
import { GetAuthUser } from '../decorators/auth-user.decorator'; import { GetAuthUser } from '../decorators/auth-user.decorator';
import { Authenticated } from '../decorators/authenticated.decorator'; import { Authenticated, SharedLinkRoute } from '../decorators/authenticated.decorator';
import { UseValidation } from '../decorators/use-validation.decorator'; import { UseValidation } from '../decorators/use-validation.decorator';
import { UUIDParamDto } from './dto/uuid-param.dto'; import { UUIDParamDto } from './dto/uuid-param.dto';
@ApiTags('share') @ApiTags('share')
@Controller('share') @Controller('share')
@Authenticated()
@UseValidation() @UseValidation()
export class SharedLinkController { export class SharedLinkController {
constructor(private readonly service: ShareService) {} constructor(private readonly service: ShareService) {}
@Authenticated()
@Get() @Get()
getAllSharedLinks(@GetAuthUser() authUser: AuthUserDto): Promise<SharedLinkResponseDto[]> { getAllSharedLinks(@GetAuthUser() authUser: AuthUserDto): Promise<SharedLinkResponseDto[]> {
return this.service.getAll(authUser); return this.service.getAll(authUser);
} }
@Authenticated({ isShared: true }) @SharedLinkRoute()
@Get('me') @Get('me')
getMySharedLink(@GetAuthUser() authUser: AuthUserDto): Promise<SharedLinkResponseDto> { getMySharedLink(@GetAuthUser() authUser: AuthUserDto): Promise<SharedLinkResponseDto> {
return this.service.getMine(authUser); return this.service.getMine(authUser);
} }
@Authenticated()
@Get(':id') @Get(':id')
getSharedLinkById( getSharedLinkById(
@GetAuthUser() authUser: AuthUserDto, @GetAuthUser() authUser: AuthUserDto,
@ -33,13 +32,11 @@ export class SharedLinkController {
return this.service.getById(authUser, id, true); return this.service.getById(authUser, id, true);
} }
@Authenticated()
@Delete(':id') @Delete(':id')
removeSharedLink(@GetAuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<void> { removeSharedLink(@GetAuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<void> {
return this.service.remove(authUser, id); return this.service.remove(authUser, id);
} }
@Authenticated()
@Patch(':id') @Patch(':id')
editSharedLink( editSharedLink(
@GetAuthUser() authUser: AuthUserDto, @GetAuthUser() authUser: AuthUserDto,

View file

@ -14,7 +14,7 @@ import {
Header, Header,
} from '@nestjs/common'; } from '@nestjs/common';
import { UserService } from '@app/domain'; import { UserService } from '@app/domain';
import { Authenticated } from '../decorators/authenticated.decorator'; import { AdminRoute, Authenticated, PublicRoute } from '../decorators/authenticated.decorator';
import { AuthUserDto, GetAuthUser } from '../decorators/auth-user.decorator'; import { AuthUserDto, GetAuthUser } from '../decorators/auth-user.decorator';
import { CreateUserDto } from '@app/domain'; import { CreateUserDto } from '@app/domain';
import { UpdateUserDto } from '@app/domain'; import { UpdateUserDto } from '@app/domain';
@ -32,58 +32,55 @@ import { UserIdDto } from '@app/domain/user/dto/user-id.dto';
@ApiTags('User') @ApiTags('User')
@Controller('user') @Controller('user')
@Authenticated()
@UseValidation() @UseValidation()
export class UserController { export class UserController {
constructor(private service: UserService) {} constructor(private service: UserService) {}
@Authenticated()
@Get() @Get()
getAllUsers(@GetAuthUser() authUser: AuthUserDto, @Query('isAll') isAll: boolean): Promise<UserResponseDto[]> { getAllUsers(@GetAuthUser() authUser: AuthUserDto, @Query('isAll') isAll: boolean): Promise<UserResponseDto[]> {
return this.service.getAllUsers(authUser, isAll); return this.service.getAllUsers(authUser, isAll);
} }
@Authenticated()
@Get('/info/:userId') @Get('/info/:userId')
getUserById(@Param() { userId }: UserIdDto): Promise<UserResponseDto> { getUserById(@Param() { userId }: UserIdDto): Promise<UserResponseDto> {
return this.service.getUserById(userId); return this.service.getUserById(userId);
} }
@Authenticated()
@Get('me') @Get('me')
getMyUserInfo(@GetAuthUser() authUser: AuthUserDto): Promise<UserResponseDto> { getMyUserInfo(@GetAuthUser() authUser: AuthUserDto): Promise<UserResponseDto> {
return this.service.getUserInfo(authUser); return this.service.getUserInfo(authUser);
} }
@Authenticated({ admin: true }) @AdminRoute()
@Post() @Post()
createUser(@Body() createUserDto: CreateUserDto): Promise<UserResponseDto> { createUser(@Body() createUserDto: CreateUserDto): Promise<UserResponseDto> {
return this.service.createUser(createUserDto); return this.service.createUser(createUserDto);
} }
@PublicRoute()
@Get('/count') @Get('/count')
getUserCount(@Query() dto: UserCountDto): Promise<UserCountResponseDto> { getUserCount(@Query() dto: UserCountDto): Promise<UserCountResponseDto> {
return this.service.getUserCount(dto); return this.service.getUserCount(dto);
} }
@Authenticated({ admin: true }) @AdminRoute()
@Delete('/:userId') @Delete('/:userId')
deleteUser(@GetAuthUser() authUser: AuthUserDto, @Param() { userId }: UserIdDto): Promise<UserResponseDto> { deleteUser(@GetAuthUser() authUser: AuthUserDto, @Param() { userId }: UserIdDto): Promise<UserResponseDto> {
return this.service.deleteUser(authUser, userId); return this.service.deleteUser(authUser, userId);
} }
@Authenticated({ admin: true }) @AdminRoute()
@Post('/:userId/restore') @Post('/:userId/restore')
restoreUser(@GetAuthUser() authUser: AuthUserDto, @Param() { userId }: UserIdDto): Promise<UserResponseDto> { restoreUser(@GetAuthUser() authUser: AuthUserDto, @Param() { userId }: UserIdDto): Promise<UserResponseDto> {
return this.service.restoreUser(authUser, userId); return this.service.restoreUser(authUser, userId);
} }
@Authenticated()
@Put() @Put()
updateUser(@GetAuthUser() authUser: AuthUserDto, @Body() updateUserDto: UpdateUserDto): Promise<UserResponseDto> { updateUser(@GetAuthUser() authUser: AuthUserDto, @Body() updateUserDto: UpdateUserDto): Promise<UserResponseDto> {
return this.service.updateUser(authUser, updateUserDto); return this.service.updateUser(authUser, updateUserDto);
} }
@Authenticated()
@UseInterceptors(FileInterceptor('file', profileImageUploadOption)) @UseInterceptors(FileInterceptor('file', profileImageUploadOption))
@ApiConsumes('multipart/form-data') @ApiConsumes('multipart/form-data')
@ApiBody({ @ApiBody({
@ -98,7 +95,6 @@ export class UserController {
return this.service.createProfileImage(authUser, fileInfo); return this.service.createProfileImage(authUser, fileInfo);
} }
@Authenticated()
@Get('/profile-image/:userId') @Get('/profile-image/:userId')
@Header('Cache-Control', 'max-age=600') @Header('Cache-Control', 'max-age=600')
async getProfileImage(@Param() { userId }: UserIdDto, @Response({ passthrough: true }) res: Res): Promise<any> { async getProfileImage(@Param() { userId }: UserIdDto, @Response({ passthrough: true }) res: Res): Promise<any> {

View file

@ -11,8 +11,16 @@ export enum Metadata {
AUTH_ROUTE = 'auth_route', AUTH_ROUTE = 'auth_route',
ADMIN_ROUTE = 'admin_route', ADMIN_ROUTE = 'admin_route',
SHARED_ROUTE = 'shared_route', SHARED_ROUTE = 'shared_route',
PUBLIC_SECURITY = 'public_security',
} }
const adminDecorator = SetMetadata(Metadata.ADMIN_ROUTE, true);
const sharedLinkDecorators = [
SetMetadata(Metadata.SHARED_ROUTE, true),
ApiQuery({ name: 'key', type: String, required: false }),
];
export const Authenticated = (options: AuthenticatedOptions = {}) => { export const Authenticated = (options: AuthenticatedOptions = {}) => {
const decorators: MethodDecorator[] = [ const decorators: MethodDecorator[] = [
ApiBearerAuth(), ApiBearerAuth(),
@ -22,13 +30,17 @@ export const Authenticated = (options: AuthenticatedOptions = {}) => {
]; ];
if (options.admin) { if (options.admin) {
decorators.push(SetMetadata(Metadata.ADMIN_ROUTE, true)); decorators.push(adminDecorator);
} }
if (options.isShared) { if (options.isShared) {
decorators.push(SetMetadata(Metadata.SHARED_ROUTE, true)); decorators.push(...sharedLinkDecorators);
decorators.push(ApiQuery({ name: 'key', type: String, required: false }));
} }
return applyDecorators(...decorators); return applyDecorators(...decorators);
}; };
export const PublicRoute = () =>
applyDecorators(SetMetadata(Metadata.AUTH_ROUTE, false), ApiSecurity(Metadata.PUBLIC_SECURITY));
export const SharedLinkRoute = () => applyDecorators(...sharedLinkDecorators);
export const AdminRoute = () => adminDecorator;

View file

@ -1,4 +1,5 @@
import { OpenAPIObject } from '@nestjs/swagger'; import { OpenAPIObject } from '@nestjs/swagger';
import { Metadata } from '../decorators/authenticated.decorator';
export function patchOpenAPI(document: OpenAPIObject) { export function patchOpenAPI(document: OpenAPIObject) {
for (const path of Object.values(document.paths)) { for (const path of Object.values(document.paths)) {
@ -18,6 +19,10 @@ export function patchOpenAPI(document: OpenAPIObject) {
continue; continue;
} }
if ((operation.security || []).find((item) => !!item[Metadata.PUBLIC_SECURITY])) {
delete operation.security;
}
if (operation.summary === '') { if (operation.summary === '') {
delete operation.summary; delete operation.summary;
} }