1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2025-01-27 22:22:45 +01:00

refactor(server): auth route metadata ()

This commit is contained in:
Jason Rasmussen 2024-05-09 13:58:44 -04:00 committed by GitHub
parent 34d8879d32
commit 8743e17528
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 171 additions and 135 deletions

View file

@ -5806,15 +5806,6 @@
} }
}, },
"security": [ "security": [
{
"bearer": []
},
{
"cookie": []
},
{
"api_key": []
},
{ {
"bearer": [] "bearer": []
}, },
@ -5942,15 +5933,6 @@
} }
}, },
"security": [ "security": [
{
"bearer": []
},
{
"cookie": []
},
{
"api_key": []
},
{ {
"bearer": [] "bearer": []
}, },

View file

@ -15,21 +15,23 @@ import { UUIDParamDto } from 'src/validation';
@ApiTags('Activity') @ApiTags('Activity')
@Controller('activity') @Controller('activity')
@Authenticated()
export class ActivityController { export class ActivityController {
constructor(private service: ActivityService) {} constructor(private service: ActivityService) {}
@Get() @Get()
@Authenticated()
getActivities(@Auth() auth: AuthDto, @Query() dto: ActivitySearchDto): Promise<ActivityResponseDto[]> { getActivities(@Auth() auth: AuthDto, @Query() dto: ActivitySearchDto): Promise<ActivityResponseDto[]> {
return this.service.getAll(auth, dto); return this.service.getAll(auth, dto);
} }
@Get('statistics') @Get('statistics')
@Authenticated()
getActivityStatistics(@Auth() auth: AuthDto, @Query() dto: ActivityDto): Promise<ActivityStatisticsResponseDto> { getActivityStatistics(@Auth() auth: AuthDto, @Query() dto: ActivityDto): Promise<ActivityStatisticsResponseDto> {
return this.service.getStatistics(auth, dto); return this.service.getStatistics(auth, dto);
} }
@Post() @Post()
@Authenticated()
async createActivity( async createActivity(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@Body() dto: ActivityCreateDto, @Body() dto: ActivityCreateDto,
@ -44,6 +46,7 @@ export class ActivityController {
@Delete(':id') @Delete(':id')
@HttpCode(HttpStatus.NO_CONTENT) @HttpCode(HttpStatus.NO_CONTENT)
@Authenticated()
deleteActivity(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<void> { deleteActivity(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<void> {
return this.service.delete(auth, id); return this.service.delete(auth, id);
} }

View file

@ -12,32 +12,34 @@ import {
} from 'src/dtos/album.dto'; } from 'src/dtos/album.dto';
import { BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; import { BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
import { AuthDto } from 'src/dtos/auth.dto'; import { AuthDto } from 'src/dtos/auth.dto';
import { Auth, Authenticated, SharedLinkRoute } from 'src/middleware/auth.guard'; import { Auth, Authenticated } from 'src/middleware/auth.guard';
import { AlbumService } from 'src/services/album.service'; import { AlbumService } from 'src/services/album.service';
import { ParseMeUUIDPipe, UUIDParamDto } from 'src/validation'; import { ParseMeUUIDPipe, UUIDParamDto } from 'src/validation';
@ApiTags('Album') @ApiTags('Album')
@Controller('album') @Controller('album')
@Authenticated()
export class AlbumController { export class AlbumController {
constructor(private service: AlbumService) {} constructor(private service: AlbumService) {}
@Get('count') @Get('count')
@Authenticated()
getAlbumCount(@Auth() auth: AuthDto): Promise<AlbumCountResponseDto> { getAlbumCount(@Auth() auth: AuthDto): Promise<AlbumCountResponseDto> {
return this.service.getCount(auth); return this.service.getCount(auth);
} }
@Get() @Get()
@Authenticated()
getAllAlbums(@Auth() auth: AuthDto, @Query() query: GetAlbumsDto): Promise<AlbumResponseDto[]> { getAllAlbums(@Auth() auth: AuthDto, @Query() query: GetAlbumsDto): Promise<AlbumResponseDto[]> {
return this.service.getAll(auth, query); return this.service.getAll(auth, query);
} }
@Post() @Post()
@Authenticated()
createAlbum(@Auth() auth: AuthDto, @Body() dto: CreateAlbumDto): Promise<AlbumResponseDto> { createAlbum(@Auth() auth: AuthDto, @Body() dto: CreateAlbumDto): Promise<AlbumResponseDto> {
return this.service.create(auth, dto); return this.service.create(auth, dto);
} }
@SharedLinkRoute() @Authenticated({ sharedLink: true })
@Get(':id') @Get(':id')
getAlbumInfo( getAlbumInfo(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@ -48,6 +50,7 @@ export class AlbumController {
} }
@Patch(':id') @Patch(':id')
@Authenticated()
updateAlbumInfo( updateAlbumInfo(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@Param() { id }: UUIDParamDto, @Param() { id }: UUIDParamDto,
@ -57,12 +60,13 @@ export class AlbumController {
} }
@Delete(':id') @Delete(':id')
@Authenticated()
deleteAlbum(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto) { deleteAlbum(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto) {
return this.service.delete(auth, id); return this.service.delete(auth, id);
} }
@SharedLinkRoute()
@Put(':id/assets') @Put(':id/assets')
@Authenticated({ sharedLink: true })
addAssetsToAlbum( addAssetsToAlbum(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@Param() { id }: UUIDParamDto, @Param() { id }: UUIDParamDto,
@ -72,6 +76,7 @@ export class AlbumController {
} }
@Delete(':id/assets') @Delete(':id/assets')
@Authenticated()
removeAssetFromAlbum( removeAssetFromAlbum(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@Body() dto: BulkIdsDto, @Body() dto: BulkIdsDto,
@ -81,6 +86,7 @@ export class AlbumController {
} }
@Put(':id/users') @Put(':id/users')
@Authenticated()
addUsersToAlbum( addUsersToAlbum(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@Param() { id }: UUIDParamDto, @Param() { id }: UUIDParamDto,
@ -90,6 +96,7 @@ export class AlbumController {
} }
@Put(':id/user/:userId') @Put(':id/user/:userId')
@Authenticated()
updateAlbumUser( updateAlbumUser(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@Param() { id }: UUIDParamDto, @Param() { id }: UUIDParamDto,
@ -100,6 +107,7 @@ export class AlbumController {
} }
@Delete(':id/user/:userId') @Delete(':id/user/:userId')
@Authenticated()
removeUserFromAlbum( removeUserFromAlbum(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@Param() { id }: UUIDParamDto, @Param() { id }: UUIDParamDto,

View file

@ -8,26 +8,29 @@ import { UUIDParamDto } from 'src/validation';
@ApiTags('API Key') @ApiTags('API Key')
@Controller('api-key') @Controller('api-key')
@Authenticated()
export class APIKeyController { export class APIKeyController {
constructor(private service: APIKeyService) {} constructor(private service: APIKeyService) {}
@Post() @Post()
@Authenticated()
createApiKey(@Auth() auth: AuthDto, @Body() dto: APIKeyCreateDto): Promise<APIKeyCreateResponseDto> { createApiKey(@Auth() auth: AuthDto, @Body() dto: APIKeyCreateDto): Promise<APIKeyCreateResponseDto> {
return this.service.create(auth, dto); return this.service.create(auth, dto);
} }
@Get() @Get()
@Authenticated()
getApiKeys(@Auth() auth: AuthDto): Promise<APIKeyResponseDto[]> { getApiKeys(@Auth() auth: AuthDto): Promise<APIKeyResponseDto[]> {
return this.service.getAll(auth); return this.service.getAll(auth);
} }
@Get(':id') @Get(':id')
@Authenticated()
getApiKey(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<APIKeyResponseDto> { getApiKey(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<APIKeyResponseDto> {
return this.service.getById(auth, id); return this.service.getById(auth, id);
} }
@Put(':id') @Put(':id')
@Authenticated()
updateApiKey( updateApiKey(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@Param() { id }: UUIDParamDto, @Param() { id }: UUIDParamDto,
@ -37,6 +40,7 @@ export class APIKeyController {
} }
@Delete(':id') @Delete(':id')
@Authenticated()
deleteApiKey(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<void> { deleteApiKey(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<void> {
return this.service.delete(auth, id); return this.service.delete(auth, id);
} }

View file

@ -1,6 +1,5 @@
import { Controller, Get, Header } from '@nestjs/common'; import { Controller, Get, Header } from '@nestjs/common';
import { ApiExcludeEndpoint } from '@nestjs/swagger'; import { ApiExcludeEndpoint } from '@nestjs/swagger';
import { PublicRoute } from 'src/middleware/auth.guard';
import { SystemConfigService } from 'src/services/system-config.service'; import { SystemConfigService } from 'src/services/system-config.service';
@Controller() @Controller()
@ -18,7 +17,6 @@ export class AppController {
} }
@ApiExcludeEndpoint() @ApiExcludeEndpoint()
@PublicRoute()
@Get('custom.css') @Get('custom.css')
@Header('Content-Type', 'text/css') @Header('Content-Type', 'text/css')
getCustomCss() { getCustomCss() {

View file

@ -31,7 +31,7 @@ import {
} from 'src/dtos/asset-v1.dto'; } from 'src/dtos/asset-v1.dto';
import { AuthDto, ImmichHeader } from 'src/dtos/auth.dto'; import { AuthDto, ImmichHeader } from 'src/dtos/auth.dto';
import { AssetUploadInterceptor } from 'src/middleware/asset-upload.interceptor'; import { AssetUploadInterceptor } from 'src/middleware/asset-upload.interceptor';
import { Auth, Authenticated, FileResponse, SharedLinkRoute } from 'src/middleware/auth.guard'; import { Auth, Authenticated, FileResponse } from 'src/middleware/auth.guard';
import { FileUploadInterceptor, ImmichFile, Route, mapToUploadFile } from 'src/middleware/file-upload.interceptor'; import { FileUploadInterceptor, ImmichFile, Route, mapToUploadFile } from 'src/middleware/file-upload.interceptor';
import { AssetServiceV1 } from 'src/services/asset-v1.service'; import { AssetServiceV1 } from 'src/services/asset-v1.service';
import { sendFile } from 'src/utils/file'; import { sendFile } from 'src/utils/file';
@ -45,11 +45,9 @@ interface UploadFiles {
@ApiTags('Asset') @ApiTags('Asset')
@Controller(Route.ASSET) @Controller(Route.ASSET)
@Authenticated()
export class AssetControllerV1 { export class AssetControllerV1 {
constructor(private service: AssetServiceV1) {} constructor(private service: AssetServiceV1) {}
@SharedLinkRoute()
@Post('upload') @Post('upload')
@UseInterceptors(AssetUploadInterceptor, FileUploadInterceptor) @UseInterceptors(AssetUploadInterceptor, FileUploadInterceptor)
@ApiConsumes('multipart/form-data') @ApiConsumes('multipart/form-data')
@ -58,10 +56,8 @@ export class AssetControllerV1 {
description: 'sha1 checksum that can be used for duplicate detection before the file is uploaded', description: 'sha1 checksum that can be used for duplicate detection before the file is uploaded',
required: false, required: false,
}) })
@ApiBody({ @ApiBody({ description: 'Asset Upload Information', type: CreateAssetDto })
description: 'Asset Upload Information', @Authenticated({ sharedLink: true })
type: CreateAssetDto,
})
async uploadFile( async uploadFile(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@UploadedFiles(new ParseFilePipe({ validators: [new FileNotEmptyValidator(['assetData'])] })) files: UploadFiles, @UploadedFiles(new ParseFilePipe({ validators: [new FileNotEmptyValidator(['assetData'])] })) files: UploadFiles,
@ -89,9 +85,9 @@ export class AssetControllerV1 {
return responseDto; return responseDto;
} }
@SharedLinkRoute()
@Get('/file/:id') @Get('/file/:id')
@FileResponse() @FileResponse()
@Authenticated({ sharedLink: true })
async serveFile( async serveFile(
@Res() res: Response, @Res() res: Response,
@Next() next: NextFunction, @Next() next: NextFunction,
@ -102,9 +98,9 @@ export class AssetControllerV1 {
await sendFile(res, next, () => this.service.serveFile(auth, id, dto)); await sendFile(res, next, () => this.service.serveFile(auth, id, dto));
} }
@SharedLinkRoute()
@Get('/thumbnail/:id') @Get('/thumbnail/:id')
@FileResponse() @FileResponse()
@Authenticated({ sharedLink: true })
async getAssetThumbnail( async getAssetThumbnail(
@Res() res: Response, @Res() res: Response,
@Next() next: NextFunction, @Next() next: NextFunction,
@ -125,6 +121,7 @@ export class AssetControllerV1 {
required: false, required: false,
schema: { type: 'string' }, schema: { type: 'string' },
}) })
@Authenticated()
getAllAssets(@Auth() auth: AuthDto, @Query() dto: AssetSearchDto): Promise<AssetResponseDto[]> { getAllAssets(@Auth() auth: AuthDto, @Query() dto: AssetSearchDto): Promise<AssetResponseDto[]> {
return this.service.getAllAssets(auth, dto); return this.service.getAllAssets(auth, dto);
} }
@ -134,6 +131,7 @@ export class AssetControllerV1 {
*/ */
@Post('/exist') @Post('/exist')
@HttpCode(HttpStatus.OK) @HttpCode(HttpStatus.OK)
@Authenticated()
checkExistingAssets( checkExistingAssets(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@Body() dto: CheckExistingAssetsDto, @Body() dto: CheckExistingAssetsDto,
@ -146,6 +144,7 @@ export class AssetControllerV1 {
*/ */
@Post('/bulk-upload-check') @Post('/bulk-upload-check')
@HttpCode(HttpStatus.OK) @HttpCode(HttpStatus.OK)
@Authenticated()
checkBulkUpload( checkBulkUpload(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@Body() dto: AssetBulkUploadCheckDto, @Body() dto: AssetBulkUploadCheckDto,

View file

@ -14,28 +14,30 @@ import {
import { AuthDto } from 'src/dtos/auth.dto'; import { AuthDto } from 'src/dtos/auth.dto';
import { MapMarkerDto, MapMarkerResponseDto, MemoryLaneDto } from 'src/dtos/search.dto'; import { MapMarkerDto, MapMarkerResponseDto, MemoryLaneDto } from 'src/dtos/search.dto';
import { UpdateStackParentDto } from 'src/dtos/stack.dto'; import { UpdateStackParentDto } from 'src/dtos/stack.dto';
import { Auth, Authenticated, SharedLinkRoute } from 'src/middleware/auth.guard'; import { Auth, Authenticated } from 'src/middleware/auth.guard';
import { Route } from 'src/middleware/file-upload.interceptor'; import { Route } from 'src/middleware/file-upload.interceptor';
import { AssetService } from 'src/services/asset.service'; import { AssetService } from 'src/services/asset.service';
import { UUIDParamDto } from 'src/validation'; import { UUIDParamDto } from 'src/validation';
@ApiTags('Asset') @ApiTags('Asset')
@Controller(Route.ASSET) @Controller(Route.ASSET)
@Authenticated()
export class AssetController { export class AssetController {
constructor(private service: AssetService) {} constructor(private service: AssetService) {}
@Get('map-marker') @Get('map-marker')
@Authenticated()
getMapMarkers(@Auth() auth: AuthDto, @Query() options: MapMarkerDto): Promise<MapMarkerResponseDto[]> { getMapMarkers(@Auth() auth: AuthDto, @Query() options: MapMarkerDto): Promise<MapMarkerResponseDto[]> {
return this.service.getMapMarkers(auth, options); return this.service.getMapMarkers(auth, options);
} }
@Get('memory-lane') @Get('memory-lane')
@Authenticated()
getMemoryLane(@Auth() auth: AuthDto, @Query() dto: MemoryLaneDto): Promise<MemoryLaneResponseDto[]> { getMemoryLane(@Auth() auth: AuthDto, @Query() dto: MemoryLaneDto): Promise<MemoryLaneResponseDto[]> {
return this.service.getMemoryLane(auth, dto); return this.service.getMemoryLane(auth, dto);
} }
@Get('random') @Get('random')
@Authenticated()
getRandom(@Auth() auth: AuthDto, @Query() dto: RandomAssetsDto): Promise<AssetResponseDto[]> { getRandom(@Auth() auth: AuthDto, @Query() dto: RandomAssetsDto): Promise<AssetResponseDto[]> {
return this.service.getRandom(auth, dto.count ?? 1); return this.service.getRandom(auth, dto.count ?? 1);
} }
@ -44,46 +46,53 @@ 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.
*/ */
@Get('/device/:deviceId') @Get('/device/:deviceId')
@Authenticated()
getAllUserAssetsByDeviceId(@Auth() auth: AuthDto, @Param() { deviceId }: DeviceIdDto) { getAllUserAssetsByDeviceId(@Auth() auth: AuthDto, @Param() { deviceId }: DeviceIdDto) {
return this.service.getUserAssetsByDeviceId(auth, deviceId); return this.service.getUserAssetsByDeviceId(auth, deviceId);
} }
@Get('statistics') @Get('statistics')
@Authenticated()
getAssetStatistics(@Auth() auth: AuthDto, @Query() dto: AssetStatsDto): Promise<AssetStatsResponseDto> { getAssetStatistics(@Auth() auth: AuthDto, @Query() dto: AssetStatsDto): Promise<AssetStatsResponseDto> {
return this.service.getStatistics(auth, dto); return this.service.getStatistics(auth, dto);
} }
@Post('jobs') @Post('jobs')
@HttpCode(HttpStatus.NO_CONTENT) @HttpCode(HttpStatus.NO_CONTENT)
@Authenticated()
runAssetJobs(@Auth() auth: AuthDto, @Body() dto: AssetJobsDto): Promise<void> { runAssetJobs(@Auth() auth: AuthDto, @Body() dto: AssetJobsDto): Promise<void> {
return this.service.run(auth, dto); return this.service.run(auth, dto);
} }
@Put() @Put()
@HttpCode(HttpStatus.NO_CONTENT) @HttpCode(HttpStatus.NO_CONTENT)
@Authenticated()
updateAssets(@Auth() auth: AuthDto, @Body() dto: AssetBulkUpdateDto): Promise<void> { updateAssets(@Auth() auth: AuthDto, @Body() dto: AssetBulkUpdateDto): Promise<void> {
return this.service.updateAll(auth, dto); return this.service.updateAll(auth, dto);
} }
@Delete() @Delete()
@HttpCode(HttpStatus.NO_CONTENT) @HttpCode(HttpStatus.NO_CONTENT)
@Authenticated()
deleteAssets(@Auth() auth: AuthDto, @Body() dto: AssetBulkDeleteDto): Promise<void> { deleteAssets(@Auth() auth: AuthDto, @Body() dto: AssetBulkDeleteDto): Promise<void> {
return this.service.deleteAll(auth, dto); return this.service.deleteAll(auth, dto);
} }
@Put('stack/parent') @Put('stack/parent')
@HttpCode(HttpStatus.OK) @HttpCode(HttpStatus.OK)
@Authenticated()
updateStackParent(@Auth() auth: AuthDto, @Body() dto: UpdateStackParentDto): Promise<void> { updateStackParent(@Auth() auth: AuthDto, @Body() dto: UpdateStackParentDto): Promise<void> {
return this.service.updateStackParent(auth, dto); return this.service.updateStackParent(auth, dto);
} }
@SharedLinkRoute()
@Get(':id') @Get(':id')
@Authenticated({ sharedLink: true })
getAssetInfo(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<AssetResponseDto> { getAssetInfo(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<AssetResponseDto> {
return this.service.get(auth, id) as Promise<AssetResponseDto>; return this.service.get(auth, id) as Promise<AssetResponseDto>;
} }
@Put(':id') @Put(':id')
@Authenticated()
updateAsset( updateAsset(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@Param() { id }: UUIDParamDto, @Param() { id }: UUIDParamDto,

View file

@ -7,11 +7,11 @@ import { AuditService } from 'src/services/audit.service';
@ApiTags('Audit') @ApiTags('Audit')
@Controller('audit') @Controller('audit')
@Authenticated()
export class AuditController { export class AuditController {
constructor(private service: AuditService) {} constructor(private service: AuditService) {}
@Get('deletes') @Get('deletes')
@Authenticated()
getAuditDeletes(@Auth() auth: AuthDto, @Query() dto: AuditDeletesDto): Promise<AuditDeletesResponseDto> { getAuditDeletes(@Auth() auth: AuthDto, @Query() dto: AuditDeletesDto): Promise<AuditDeletesResponseDto> {
return this.service.getDeletes(auth, dto); return this.service.getDeletes(auth, dto);
} }

View file

@ -13,17 +13,15 @@ import {
ValidateAccessTokenResponseDto, ValidateAccessTokenResponseDto,
} from 'src/dtos/auth.dto'; } from 'src/dtos/auth.dto';
import { UserResponseDto, mapUser } from 'src/dtos/user.dto'; import { UserResponseDto, mapUser } from 'src/dtos/user.dto';
import { Auth, Authenticated, GetLoginDetails, PublicRoute } from 'src/middleware/auth.guard'; import { Auth, Authenticated, GetLoginDetails } from 'src/middleware/auth.guard';
import { AuthService, LoginDetails } from 'src/services/auth.service'; import { AuthService, LoginDetails } from 'src/services/auth.service';
import { respondWithCookie, respondWithoutCookie } from 'src/utils/response'; import { respondWithCookie, respondWithoutCookie } from 'src/utils/response';
@ApiTags('Authentication') @ApiTags('Authentication')
@Controller('auth') @Controller('auth')
@Authenticated()
export class AuthController { export class AuthController {
constructor(private service: AuthService) {} constructor(private service: AuthService) {}
@PublicRoute()
@Post('login') @Post('login')
async login( async login(
@Body() loginCredential: LoginCredentialDto, @Body() loginCredential: LoginCredentialDto,
@ -41,7 +39,6 @@ export class AuthController {
}); });
} }
@PublicRoute()
@Post('admin-sign-up') @Post('admin-sign-up')
signUpAdmin(@Body() dto: SignUpDto): Promise<UserResponseDto> { signUpAdmin(@Body() dto: SignUpDto): Promise<UserResponseDto> {
return this.service.adminSignUp(dto); return this.service.adminSignUp(dto);
@ -49,18 +46,21 @@ export class AuthController {
@Post('validateToken') @Post('validateToken')
@HttpCode(HttpStatus.OK) @HttpCode(HttpStatus.OK)
@Authenticated()
validateAccessToken(): ValidateAccessTokenResponseDto { validateAccessToken(): ValidateAccessTokenResponseDto {
return { authStatus: true }; return { authStatus: true };
} }
@Post('change-password') @Post('change-password')
@HttpCode(HttpStatus.OK) @HttpCode(HttpStatus.OK)
@Authenticated()
changePassword(@Auth() auth: AuthDto, @Body() dto: ChangePasswordDto): Promise<UserResponseDto> { changePassword(@Auth() auth: AuthDto, @Body() dto: ChangePasswordDto): Promise<UserResponseDto> {
return this.service.changePassword(auth, dto).then(mapUser); return this.service.changePassword(auth, dto).then(mapUser);
} }
@Post('logout') @Post('logout')
@HttpCode(HttpStatus.OK) @HttpCode(HttpStatus.OK)
@Authenticated()
async logout( async logout(
@Req() request: Request, @Req() request: Request,
@Res({ passthrough: true }) res: Response, @Res({ passthrough: true }) res: Response,

View file

@ -4,35 +4,34 @@ import { NextFunction, Response } from 'express';
import { AssetIdsDto } from 'src/dtos/asset.dto'; import { AssetIdsDto } from 'src/dtos/asset.dto';
import { AuthDto } from 'src/dtos/auth.dto'; import { AuthDto } from 'src/dtos/auth.dto';
import { DownloadInfoDto, DownloadResponseDto } from 'src/dtos/download.dto'; import { DownloadInfoDto, DownloadResponseDto } from 'src/dtos/download.dto';
import { Auth, Authenticated, FileResponse, SharedLinkRoute } from 'src/middleware/auth.guard'; import { Auth, Authenticated, FileResponse } from 'src/middleware/auth.guard';
import { DownloadService } from 'src/services/download.service'; import { DownloadService } from 'src/services/download.service';
import { asStreamableFile, sendFile } from 'src/utils/file'; import { asStreamableFile, sendFile } from 'src/utils/file';
import { UUIDParamDto } from 'src/validation'; import { UUIDParamDto } from 'src/validation';
@ApiTags('Download') @ApiTags('Download')
@Controller('download') @Controller('download')
@Authenticated()
export class DownloadController { export class DownloadController {
constructor(private service: DownloadService) {} constructor(private service: DownloadService) {}
@SharedLinkRoute()
@Post('info') @Post('info')
@Authenticated({ sharedLink: true })
getDownloadInfo(@Auth() auth: AuthDto, @Body() dto: DownloadInfoDto): Promise<DownloadResponseDto> { getDownloadInfo(@Auth() auth: AuthDto, @Body() dto: DownloadInfoDto): Promise<DownloadResponseDto> {
return this.service.getDownloadInfo(auth, dto); return this.service.getDownloadInfo(auth, dto);
} }
@SharedLinkRoute()
@Post('archive') @Post('archive')
@HttpCode(HttpStatus.OK) @HttpCode(HttpStatus.OK)
@FileResponse() @FileResponse()
@Authenticated({ sharedLink: true })
downloadArchive(@Auth() auth: AuthDto, @Body() dto: AssetIdsDto): Promise<StreamableFile> { downloadArchive(@Auth() auth: AuthDto, @Body() dto: AssetIdsDto): Promise<StreamableFile> {
return this.service.downloadArchive(auth, dto).then(asStreamableFile); return this.service.downloadArchive(auth, dto).then(asStreamableFile);
} }
@SharedLinkRoute()
@Post('asset/:id') @Post('asset/:id')
@HttpCode(HttpStatus.OK) @HttpCode(HttpStatus.OK)
@FileResponse() @FileResponse()
@Authenticated({ sharedLink: true })
async downloadFile( async downloadFile(
@Res() res: Response, @Res() res: Response,
@Next() next: NextFunction, @Next() next: NextFunction,

View file

@ -8,16 +8,17 @@ import { UUIDParamDto } from 'src/validation';
@ApiTags('Face') @ApiTags('Face')
@Controller('face') @Controller('face')
@Authenticated()
export class FaceController { export class FaceController {
constructor(private service: PersonService) {} constructor(private service: PersonService) {}
@Get() @Get()
@Authenticated()
getFaces(@Auth() auth: AuthDto, @Query() dto: FaceDto): Promise<AssetFaceResponseDto[]> { getFaces(@Auth() auth: AuthDto, @Query() dto: FaceDto): Promise<AssetFaceResponseDto[]> {
return this.service.getFacesById(auth, dto); return this.service.getFacesById(auth, dto);
} }
@Put(':id') @Put(':id')
@Authenticated()
reassignFacesById( reassignFacesById(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@Param() { id }: UUIDParamDto, @Param() { id }: UUIDParamDto,

View file

@ -1,29 +1,28 @@
import { Body, Controller, Get, Post } from '@nestjs/common'; import { Body, Controller, Get, Post } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger'; import { ApiTags } from '@nestjs/swagger';
import { FileChecksumDto, FileChecksumResponseDto, FileReportDto, FileReportFixDto } from 'src/dtos/audit.dto'; import { FileChecksumDto, FileChecksumResponseDto, FileReportDto, FileReportFixDto } from 'src/dtos/audit.dto';
import { AdminRoute, Authenticated } from 'src/middleware/auth.guard'; import { Authenticated } from 'src/middleware/auth.guard';
import { AuditService } from 'src/services/audit.service'; import { AuditService } from 'src/services/audit.service';
@ApiTags('File Report') @ApiTags('File Report')
@Controller('report') @Controller('report')
@Authenticated()
export class ReportController { export class ReportController {
constructor(private service: AuditService) {} constructor(private service: AuditService) {}
@AdminRoute()
@Get() @Get()
@Authenticated({ admin: true })
getAuditFiles(): Promise<FileReportDto> { getAuditFiles(): Promise<FileReportDto> {
return this.service.getFileReport(); return this.service.getFileReport();
} }
@AdminRoute() @Post('checksum')
@Post('/checksum') @Authenticated({ admin: true })
getFileChecksums(@Body() dto: FileChecksumDto): Promise<FileChecksumResponseDto[]> { getFileChecksums(@Body() dto: FileChecksumDto): Promise<FileChecksumResponseDto[]> {
return this.service.getChecksums(dto); return this.service.getChecksums(dto);
} }
@AdminRoute() @Post('fix')
@Post('/fix') @Authenticated({ admin: true })
fixAuditFiles(@Body() dto: FileReportFixDto): Promise<void> { fixAuditFiles(@Body() dto: FileReportFixDto): Promise<void> {
return this.service.fixItems(dto.items); return this.service.fixItems(dto.items);
} }

View file

@ -6,16 +6,17 @@ import { JobService } from 'src/services/job.service';
@ApiTags('Job') @ApiTags('Job')
@Controller('jobs') @Controller('jobs')
@Authenticated({ admin: true })
export class JobController { export class JobController {
constructor(private service: JobService) {} constructor(private service: JobService) {}
@Get() @Get()
@Authenticated({ admin: true })
getAllJobsStatus(): Promise<AllJobStatusResponseDto> { getAllJobsStatus(): Promise<AllJobStatusResponseDto> {
return this.service.getAllJobsStatus(); return this.service.getAllJobsStatus();
} }
@Put(':id') @Put(':id')
@Authenticated({ admin: true })
sendJobCommand(@Param() { id }: JobIdParamDto, @Body() dto: JobCommandDto): Promise<JobStatusDto> { sendJobCommand(@Param() { id }: JobIdParamDto, @Body() dto: JobCommandDto): Promise<JobStatusDto> {
return this.service.handleCommand(id, dto); return this.service.handleCommand(id, dto);
} }

View file

@ -10,39 +10,42 @@ import {
ValidateLibraryDto, ValidateLibraryDto,
ValidateLibraryResponseDto, ValidateLibraryResponseDto,
} from 'src/dtos/library.dto'; } from 'src/dtos/library.dto';
import { AdminRoute, Authenticated } from 'src/middleware/auth.guard'; import { Authenticated } from 'src/middleware/auth.guard';
import { LibraryService } from 'src/services/library.service'; import { LibraryService } from 'src/services/library.service';
import { UUIDParamDto } from 'src/validation'; import { UUIDParamDto } from 'src/validation';
@ApiTags('Library') @ApiTags('Library')
@Controller('library') @Controller('library')
@Authenticated()
@AdminRoute()
export class LibraryController { export class LibraryController {
constructor(private service: LibraryService) {} constructor(private service: LibraryService) {}
@Get() @Get()
@Authenticated({ admin: true })
getAllLibraries(@Query() dto: SearchLibraryDto): Promise<LibraryResponseDto[]> { getAllLibraries(@Query() dto: SearchLibraryDto): Promise<LibraryResponseDto[]> {
return this.service.getAll(dto); return this.service.getAll(dto);
} }
@Post() @Post()
@Authenticated({ admin: true })
createLibrary(@Body() dto: CreateLibraryDto): Promise<LibraryResponseDto> { createLibrary(@Body() dto: CreateLibraryDto): Promise<LibraryResponseDto> {
return this.service.create(dto); return this.service.create(dto);
} }
@Put(':id') @Put(':id')
@Authenticated({ admin: true })
updateLibrary(@Param() { id }: UUIDParamDto, @Body() dto: UpdateLibraryDto): Promise<LibraryResponseDto> { updateLibrary(@Param() { id }: UUIDParamDto, @Body() dto: UpdateLibraryDto): Promise<LibraryResponseDto> {
return this.service.update(id, dto); return this.service.update(id, dto);
} }
@Get(':id') @Get(':id')
@Authenticated({ admin: true })
getLibrary(@Param() { id }: UUIDParamDto): Promise<LibraryResponseDto> { getLibrary(@Param() { id }: UUIDParamDto): Promise<LibraryResponseDto> {
return this.service.get(id); return this.service.get(id);
} }
@Post(':id/validate') @Post(':id/validate')
@HttpCode(200) @HttpCode(200)
@Authenticated({ admin: true })
// TODO: change endpoint to validate current settings instead // TODO: change endpoint to validate current settings instead
validate(@Param() { id }: UUIDParamDto, @Body() dto: ValidateLibraryDto): Promise<ValidateLibraryResponseDto> { validate(@Param() { id }: UUIDParamDto, @Body() dto: ValidateLibraryDto): Promise<ValidateLibraryResponseDto> {
return this.service.validate(id, dto); return this.service.validate(id, dto);
@ -50,23 +53,27 @@ export class LibraryController {
@Delete(':id') @Delete(':id')
@HttpCode(HttpStatus.NO_CONTENT) @HttpCode(HttpStatus.NO_CONTENT)
@Authenticated({ admin: true })
deleteLibrary(@Param() { id }: UUIDParamDto): Promise<void> { deleteLibrary(@Param() { id }: UUIDParamDto): Promise<void> {
return this.service.delete(id); return this.service.delete(id);
} }
@Get(':id/statistics') @Get(':id/statistics')
@Authenticated({ admin: true })
getLibraryStatistics(@Param() { id }: UUIDParamDto): Promise<LibraryStatsResponseDto> { getLibraryStatistics(@Param() { id }: UUIDParamDto): Promise<LibraryStatsResponseDto> {
return this.service.getStatistics(id); return this.service.getStatistics(id);
} }
@Post(':id/scan') @Post(':id/scan')
@HttpCode(HttpStatus.NO_CONTENT) @HttpCode(HttpStatus.NO_CONTENT)
@Authenticated({ admin: true })
scanLibrary(@Param() { id }: UUIDParamDto, @Body() dto: ScanLibraryDto) { scanLibrary(@Param() { id }: UUIDParamDto, @Body() dto: ScanLibraryDto) {
return this.service.queueScan(id, dto); return this.service.queueScan(id, dto);
} }
@Post(':id/removeOffline') @Post(':id/removeOffline')
@HttpCode(HttpStatus.NO_CONTENT) @HttpCode(HttpStatus.NO_CONTENT)
@Authenticated({ admin: true })
removeOfflineFiles(@Param() { id }: UUIDParamDto) { removeOfflineFiles(@Param() { id }: UUIDParamDto) {
return this.service.queueRemoveOffline(id); return this.service.queueRemoveOffline(id);
} }

View file

@ -9,26 +9,29 @@ import { UUIDParamDto } from 'src/validation';
@ApiTags('Memory') @ApiTags('Memory')
@Controller('memories') @Controller('memories')
@Authenticated()
export class MemoryController { export class MemoryController {
constructor(private service: MemoryService) {} constructor(private service: MemoryService) {}
@Get() @Get()
@Authenticated()
searchMemories(@Auth() auth: AuthDto): Promise<MemoryResponseDto[]> { searchMemories(@Auth() auth: AuthDto): Promise<MemoryResponseDto[]> {
return this.service.search(auth); return this.service.search(auth);
} }
@Post() @Post()
@Authenticated()
createMemory(@Auth() auth: AuthDto, @Body() dto: MemoryCreateDto): Promise<MemoryResponseDto> { createMemory(@Auth() auth: AuthDto, @Body() dto: MemoryCreateDto): Promise<MemoryResponseDto> {
return this.service.create(auth, dto); return this.service.create(auth, dto);
} }
@Get(':id') @Get(':id')
@Authenticated()
getMemory(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<MemoryResponseDto> { getMemory(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<MemoryResponseDto> {
return this.service.get(auth, id); return this.service.get(auth, id);
} }
@Put(':id') @Put(':id')
@Authenticated()
updateMemory( updateMemory(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@Param() { id }: UUIDParamDto, @Param() { id }: UUIDParamDto,
@ -39,11 +42,13 @@ export class MemoryController {
@Delete(':id') @Delete(':id')
@HttpCode(HttpStatus.NO_CONTENT) @HttpCode(HttpStatus.NO_CONTENT)
@Authenticated()
deleteMemory(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<void> { deleteMemory(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<void> {
return this.service.remove(auth, id); return this.service.remove(auth, id);
} }
@Put(':id/assets') @Put(':id/assets')
@Authenticated()
addMemoryAssets( addMemoryAssets(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@Param() { id }: UUIDParamDto, @Param() { id }: UUIDParamDto,
@ -54,6 +59,7 @@ export class MemoryController {
@Delete(':id/assets') @Delete(':id/assets')
@HttpCode(HttpStatus.OK) @HttpCode(HttpStatus.OK)
@Authenticated()
removeMemoryAssets( removeMemoryAssets(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@Body() dto: BulkIdsDto, @Body() dto: BulkIdsDto,

View file

@ -11,17 +11,15 @@ import {
OAuthConfigDto, OAuthConfigDto,
} from 'src/dtos/auth.dto'; } from 'src/dtos/auth.dto';
import { UserResponseDto } from 'src/dtos/user.dto'; import { UserResponseDto } from 'src/dtos/user.dto';
import { Auth, Authenticated, GetLoginDetails, PublicRoute } from 'src/middleware/auth.guard'; import { Auth, Authenticated, GetLoginDetails } from 'src/middleware/auth.guard';
import { AuthService, LoginDetails } from 'src/services/auth.service'; import { AuthService, LoginDetails } from 'src/services/auth.service';
import { respondWithCookie } from 'src/utils/response'; import { respondWithCookie } from 'src/utils/response';
@ApiTags('OAuth') @ApiTags('OAuth')
@Controller('oauth') @Controller('oauth')
@Authenticated()
export class OAuthController { export class OAuthController {
constructor(private service: AuthService) {} constructor(private service: AuthService) {}
@PublicRoute()
@Get('mobile-redirect') @Get('mobile-redirect')
@Redirect() @Redirect()
redirectOAuthToMobile(@Req() request: Request) { redirectOAuthToMobile(@Req() request: Request) {
@ -31,13 +29,11 @@ export class OAuthController {
}; };
} }
@PublicRoute()
@Post('authorize') @Post('authorize')
startOAuth(@Body() dto: OAuthConfigDto): Promise<OAuthAuthorizeResponseDto> { startOAuth(@Body() dto: OAuthConfigDto): Promise<OAuthAuthorizeResponseDto> {
return this.service.authorize(dto); return this.service.authorize(dto);
} }
@PublicRoute()
@Post('callback') @Post('callback')
async finishOAuth( async finishOAuth(
@Res({ passthrough: true }) res: Response, @Res({ passthrough: true }) res: Response,
@ -56,11 +52,13 @@ export class OAuthController {
} }
@Post('link') @Post('link')
@Authenticated()
linkOAuthAccount(@Auth() auth: AuthDto, @Body() dto: OAuthCallbackDto): Promise<UserResponseDto> { linkOAuthAccount(@Auth() auth: AuthDto, @Body() dto: OAuthCallbackDto): Promise<UserResponseDto> {
return this.service.link(auth, dto); return this.service.link(auth, dto);
} }
@Post('unlink') @Post('unlink')
@Authenticated()
unlinkOAuthAccount(@Auth() auth: AuthDto): Promise<UserResponseDto> { unlinkOAuthAccount(@Auth() auth: AuthDto): Promise<UserResponseDto> {
return this.service.unlink(auth); return this.service.unlink(auth);
} }

View file

@ -9,23 +9,25 @@ import { UUIDParamDto } from 'src/validation';
@ApiTags('Partner') @ApiTags('Partner')
@Controller('partner') @Controller('partner')
@Authenticated()
export class PartnerController { export class PartnerController {
constructor(private service: PartnerService) {} constructor(private service: PartnerService) {}
@Get() @Get()
@ApiQuery({ name: 'direction', type: 'string', enum: PartnerDirection, required: true }) @ApiQuery({ name: 'direction', type: 'string', enum: PartnerDirection, required: true })
@Authenticated()
// TODO: remove 'direction' and convert to full query dto // TODO: remove 'direction' and convert to full query dto
getPartners(@Auth() auth: AuthDto, @Query('direction') direction: PartnerDirection): Promise<PartnerResponseDto[]> { getPartners(@Auth() auth: AuthDto, @Query('direction') direction: PartnerDirection): Promise<PartnerResponseDto[]> {
return this.service.getAll(auth, direction); return this.service.getAll(auth, direction);
} }
@Post(':id') @Post(':id')
@Authenticated()
createPartner(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<PartnerResponseDto> { createPartner(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<PartnerResponseDto> {
return this.service.create(auth, id); return this.service.create(auth, id);
} }
@Put(':id') @Put(':id')
@Authenticated()
updatePartner( updatePartner(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@Param() { id }: UUIDParamDto, @Param() { id }: UUIDParamDto,
@ -35,6 +37,7 @@ export class PartnerController {
} }
@Delete(':id') @Delete(':id')
@Authenticated()
removePartner(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<void> { removePartner(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<void> {
return this.service.remove(auth, id); return this.service.remove(auth, id);
} }

View file

@ -22,31 +22,35 @@ import { UUIDParamDto } from 'src/validation';
@ApiTags('Person') @ApiTags('Person')
@Controller('person') @Controller('person')
@Authenticated()
export class PersonController { export class PersonController {
constructor(private service: PersonService) {} constructor(private service: PersonService) {}
@Get() @Get()
@Authenticated()
getAllPeople(@Auth() auth: AuthDto, @Query() withHidden: PersonSearchDto): Promise<PeopleResponseDto> { getAllPeople(@Auth() auth: AuthDto, @Query() withHidden: PersonSearchDto): Promise<PeopleResponseDto> {
return this.service.getAll(auth, withHidden); return this.service.getAll(auth, withHidden);
} }
@Post() @Post()
@Authenticated()
createPerson(@Auth() auth: AuthDto, @Body() dto: PersonCreateDto): Promise<PersonResponseDto> { createPerson(@Auth() auth: AuthDto, @Body() dto: PersonCreateDto): Promise<PersonResponseDto> {
return this.service.create(auth, dto); return this.service.create(auth, dto);
} }
@Put() @Put()
@Authenticated()
updatePeople(@Auth() auth: AuthDto, @Body() dto: PeopleUpdateDto): Promise<BulkIdResponseDto[]> { updatePeople(@Auth() auth: AuthDto, @Body() dto: PeopleUpdateDto): Promise<BulkIdResponseDto[]> {
return this.service.updateAll(auth, dto); return this.service.updateAll(auth, dto);
} }
@Get(':id') @Get(':id')
@Authenticated()
getPerson(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<PersonResponseDto> { getPerson(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<PersonResponseDto> {
return this.service.getById(auth, id); return this.service.getById(auth, id);
} }
@Put(':id') @Put(':id')
@Authenticated()
updatePerson( updatePerson(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@Param() { id }: UUIDParamDto, @Param() { id }: UUIDParamDto,
@ -56,12 +60,14 @@ export class PersonController {
} }
@Get(':id/statistics') @Get(':id/statistics')
@Authenticated()
getPersonStatistics(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<PersonStatisticsResponseDto> { getPersonStatistics(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<PersonStatisticsResponseDto> {
return this.service.getStatistics(auth, id); return this.service.getStatistics(auth, id);
} }
@Get(':id/thumbnail') @Get(':id/thumbnail')
@FileResponse() @FileResponse()
@Authenticated()
async getPersonThumbnail( async getPersonThumbnail(
@Res() res: Response, @Res() res: Response,
@Next() next: NextFunction, @Next() next: NextFunction,
@ -72,11 +78,13 @@ export class PersonController {
} }
@Get(':id/assets') @Get(':id/assets')
@Authenticated()
getPersonAssets(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<AssetResponseDto[]> { getPersonAssets(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<AssetResponseDto[]> {
return this.service.getAssets(auth, id); return this.service.getAssets(auth, id);
} }
@Put(':id/reassign') @Put(':id/reassign')
@Authenticated()
reassignFaces( reassignFaces(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@Param() { id }: UUIDParamDto, @Param() { id }: UUIDParamDto,
@ -86,6 +94,7 @@ export class PersonController {
} }
@Post(':id/merge') @Post(':id/merge')
@Authenticated()
mergePerson( mergePerson(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@Param() { id }: UUIDParamDto, @Param() { id }: UUIDParamDto,

View file

@ -18,43 +18,49 @@ import { SearchService } from 'src/services/search.service';
@ApiTags('Search') @ApiTags('Search')
@Controller('search') @Controller('search')
@Authenticated()
export class SearchController { export class SearchController {
constructor(private service: SearchService) {} constructor(private service: SearchService) {}
@Post('metadata') @Post('metadata')
@HttpCode(HttpStatus.OK) @HttpCode(HttpStatus.OK)
@Authenticated()
searchMetadata(@Auth() auth: AuthDto, @Body() dto: MetadataSearchDto): Promise<SearchResponseDto> { searchMetadata(@Auth() auth: AuthDto, @Body() dto: MetadataSearchDto): Promise<SearchResponseDto> {
return this.service.searchMetadata(auth, dto); return this.service.searchMetadata(auth, dto);
} }
@Post('smart') @Post('smart')
@HttpCode(HttpStatus.OK) @HttpCode(HttpStatus.OK)
@Authenticated()
searchSmart(@Auth() auth: AuthDto, @Body() dto: SmartSearchDto): Promise<SearchResponseDto> { searchSmart(@Auth() auth: AuthDto, @Body() dto: SmartSearchDto): Promise<SearchResponseDto> {
return this.service.searchSmart(auth, dto); return this.service.searchSmart(auth, dto);
} }
@Get('explore') @Get('explore')
@Authenticated()
getExploreData(@Auth() auth: AuthDto): Promise<SearchExploreResponseDto[]> { getExploreData(@Auth() auth: AuthDto): Promise<SearchExploreResponseDto[]> {
return this.service.getExploreData(auth) as Promise<SearchExploreResponseDto[]>; return this.service.getExploreData(auth) as Promise<SearchExploreResponseDto[]>;
} }
@Get('person') @Get('person')
@Authenticated()
searchPerson(@Auth() auth: AuthDto, @Query() dto: SearchPeopleDto): Promise<PersonResponseDto[]> { searchPerson(@Auth() auth: AuthDto, @Query() dto: SearchPeopleDto): Promise<PersonResponseDto[]> {
return this.service.searchPerson(auth, dto); return this.service.searchPerson(auth, dto);
} }
@Get('places') @Get('places')
@Authenticated()
searchPlaces(@Query() dto: SearchPlacesDto): Promise<PlacesResponseDto[]> { searchPlaces(@Query() dto: SearchPlacesDto): Promise<PlacesResponseDto[]> {
return this.service.searchPlaces(dto); return this.service.searchPlaces(dto);
} }
@Get('cities') @Get('cities')
@Authenticated()
getAssetsByCity(@Auth() auth: AuthDto): Promise<AssetResponseDto[]> { getAssetsByCity(@Auth() auth: AuthDto): Promise<AssetResponseDto[]> {
return this.service.getAssetsByCity(auth); return this.service.getAssetsByCity(auth);
} }
@Get('suggestions') @Get('suggestions')
@Authenticated()
getSearchSuggestions(@Auth() auth: AuthDto, @Query() dto: SearchSuggestionRequestDto): Promise<string[]> { getSearchSuggestions(@Auth() auth: AuthDto, @Query() dto: SearchSuggestionRequestDto): Promise<string[]> {
return this.service.getSearchSuggestions(auth, dto); return this.service.getSearchSuggestions(auth, dto);
} }

View file

@ -10,57 +10,51 @@ import {
ServerThemeDto, ServerThemeDto,
ServerVersionResponseDto, ServerVersionResponseDto,
} from 'src/dtos/server-info.dto'; } from 'src/dtos/server-info.dto';
import { AdminRoute, Authenticated, PublicRoute } from 'src/middleware/auth.guard'; import { Authenticated } from 'src/middleware/auth.guard';
import { ServerInfoService } from 'src/services/server-info.service'; import { ServerInfoService } from 'src/services/server-info.service';
@ApiTags('Server Info') @ApiTags('Server Info')
@Controller('server-info') @Controller('server-info')
@Authenticated()
export class ServerInfoController { export class ServerInfoController {
constructor(private service: ServerInfoService) {} constructor(private service: ServerInfoService) {}
@Get() @Get()
@Authenticated()
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(): ServerVersionResponseDto { getServerVersion(): ServerVersionResponseDto {
return this.service.getVersion(); return this.service.getVersion();
} }
@PublicRoute()
@Get('features') @Get('features')
getServerFeatures(): Promise<ServerFeaturesDto> { getServerFeatures(): Promise<ServerFeaturesDto> {
return this.service.getFeatures(); return this.service.getFeatures();
} }
@PublicRoute()
@Get('theme') @Get('theme')
getTheme(): Promise<ServerThemeDto> { getTheme(): Promise<ServerThemeDto> {
return this.service.getTheme(); return this.service.getTheme();
} }
@PublicRoute()
@Get('config') @Get('config')
getServerConfig(): Promise<ServerConfigDto> { getServerConfig(): Promise<ServerConfigDto> {
return this.service.getConfig(); return this.service.getConfig();
} }
@AdminRoute() @Authenticated({ admin: true })
@Get('statistics') @Get('statistics')
getServerStatistics(): Promise<ServerStatsResponseDto> { getServerStatistics(): Promise<ServerStatsResponseDto> {
return this.service.getStatistics(); return this.service.getStatistics();
} }
@PublicRoute()
@Get('media-types') @Get('media-types')
getSupportedMediaTypes(): ServerMediaTypesResponseDto { getSupportedMediaTypes(): ServerMediaTypesResponseDto {
return this.service.getSupportedMediaTypes(); return this.service.getSupportedMediaTypes();

View file

@ -8,23 +8,25 @@ import { UUIDParamDto } from 'src/validation';
@ApiTags('Sessions') @ApiTags('Sessions')
@Controller('sessions') @Controller('sessions')
@Authenticated()
export class SessionController { export class SessionController {
constructor(private service: SessionService) {} constructor(private service: SessionService) {}
@Get() @Get()
@Authenticated()
getSessions(@Auth() auth: AuthDto): Promise<SessionResponseDto[]> { getSessions(@Auth() auth: AuthDto): Promise<SessionResponseDto[]> {
return this.service.getAll(auth); return this.service.getAll(auth);
} }
@Delete() @Delete()
@HttpCode(HttpStatus.NO_CONTENT) @HttpCode(HttpStatus.NO_CONTENT)
@Authenticated()
deleteAllSessions(@Auth() auth: AuthDto): Promise<void> { deleteAllSessions(@Auth() auth: AuthDto): Promise<void> {
return this.service.deleteAll(auth); return this.service.deleteAll(auth);
} }
@Delete(':id') @Delete(':id')
@HttpCode(HttpStatus.NO_CONTENT) @HttpCode(HttpStatus.NO_CONTENT)
@Authenticated()
deleteSession(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<void> { deleteSession(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<void> {
return this.service.delete(auth, id); return this.service.delete(auth, id);
} }

View file

@ -10,7 +10,7 @@ import {
SharedLinkPasswordDto, SharedLinkPasswordDto,
SharedLinkResponseDto, SharedLinkResponseDto,
} from 'src/dtos/shared-link.dto'; } from 'src/dtos/shared-link.dto';
import { Auth, Authenticated, GetLoginDetails, SharedLinkRoute } from 'src/middleware/auth.guard'; import { Auth, Authenticated, GetLoginDetails } from 'src/middleware/auth.guard';
import { LoginDetails } from 'src/services/auth.service'; import { LoginDetails } from 'src/services/auth.service';
import { SharedLinkService } from 'src/services/shared-link.service'; import { SharedLinkService } from 'src/services/shared-link.service';
import { respondWithCookie } from 'src/utils/response'; import { respondWithCookie } from 'src/utils/response';
@ -18,17 +18,17 @@ import { UUIDParamDto } from 'src/validation';
@ApiTags('Shared Link') @ApiTags('Shared Link')
@Controller('shared-link') @Controller('shared-link')
@Authenticated()
export class SharedLinkController { export class SharedLinkController {
constructor(private service: SharedLinkService) {} constructor(private service: SharedLinkService) {}
@Get() @Get()
@Authenticated()
getAllSharedLinks(@Auth() auth: AuthDto): Promise<SharedLinkResponseDto[]> { getAllSharedLinks(@Auth() auth: AuthDto): Promise<SharedLinkResponseDto[]> {
return this.service.getAll(auth); return this.service.getAll(auth);
} }
@SharedLinkRoute()
@Get('me') @Get('me')
@Authenticated({ sharedLink: true })
async getMySharedLink( async getMySharedLink(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@Query() dto: SharedLinkPasswordDto, @Query() dto: SharedLinkPasswordDto,
@ -48,16 +48,19 @@ export class SharedLinkController {
} }
@Get(':id') @Get(':id')
@Authenticated()
getSharedLinkById(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<SharedLinkResponseDto> { getSharedLinkById(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<SharedLinkResponseDto> {
return this.service.get(auth, id); return this.service.get(auth, id);
} }
@Post() @Post()
@Authenticated()
createSharedLink(@Auth() auth: AuthDto, @Body() dto: SharedLinkCreateDto) { createSharedLink(@Auth() auth: AuthDto, @Body() dto: SharedLinkCreateDto) {
return this.service.create(auth, dto); return this.service.create(auth, dto);
} }
@Patch(':id') @Patch(':id')
@Authenticated()
updateSharedLink( updateSharedLink(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@Param() { id }: UUIDParamDto, @Param() { id }: UUIDParamDto,
@ -67,12 +70,13 @@ export class SharedLinkController {
} }
@Delete(':id') @Delete(':id')
@Authenticated()
removeSharedLink(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<void> { removeSharedLink(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<void> {
return this.service.remove(auth, id); return this.service.remove(auth, id);
} }
@SharedLinkRoute()
@Put(':id/assets') @Put(':id/assets')
@Authenticated({ sharedLink: true })
addSharedLinkAssets( addSharedLinkAssets(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@Param() { id }: UUIDParamDto, @Param() { id }: UUIDParamDto,
@ -81,8 +85,8 @@ export class SharedLinkController {
return this.service.addAssets(auth, id, dto); return this.service.addAssets(auth, id, dto);
} }
@SharedLinkRoute()
@Delete(':id/assets') @Delete(':id/assets')
@Authenticated({ sharedLink: true })
removeSharedLinkAssets( removeSharedLinkAssets(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@Param() { id }: UUIDParamDto, @Param() { id }: UUIDParamDto,

View file

@ -8,18 +8,19 @@ import { SyncService } from 'src/services/sync.service';
@ApiTags('Sync') @ApiTags('Sync')
@Controller('sync') @Controller('sync')
@Authenticated()
export class SyncController { export class SyncController {
constructor(private service: SyncService) {} constructor(private service: SyncService) {}
@Post('full-sync') @Post('full-sync')
@HttpCode(HttpStatus.OK) @HttpCode(HttpStatus.OK)
@Authenticated()
getFullSyncForUser(@Auth() auth: AuthDto, @Body() dto: AssetFullSyncDto): Promise<AssetResponseDto[]> { getFullSyncForUser(@Auth() auth: AuthDto, @Body() dto: AssetFullSyncDto): Promise<AssetResponseDto[]> {
return this.service.getFullSync(auth, dto); return this.service.getFullSync(auth, dto);
} }
@Post('delta-sync') @Post('delta-sync')
@HttpCode(HttpStatus.OK) @HttpCode(HttpStatus.OK)
@Authenticated()
getDeltaSync(@Auth() auth: AuthDto, @Body() dto: AssetDeltaSyncDto): Promise<AssetDeltaSyncResponseDto> { getDeltaSync(@Auth() auth: AuthDto, @Body() dto: AssetDeltaSyncDto): Promise<AssetDeltaSyncResponseDto> {
return this.service.getDeltaSync(auth, dto); return this.service.getDeltaSync(auth, dto);
} }

View file

@ -1,37 +1,39 @@
import { Body, Controller, Get, Put, Query } from '@nestjs/common'; import { Body, Controller, Get, Put, Query } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger'; import { ApiTags } from '@nestjs/swagger';
import { MapThemeDto, SystemConfigDto, SystemConfigTemplateStorageOptionDto } from 'src/dtos/system-config.dto'; import { MapThemeDto, SystemConfigDto, SystemConfigTemplateStorageOptionDto } from 'src/dtos/system-config.dto';
import { AdminRoute, Authenticated, SharedLinkRoute } from 'src/middleware/auth.guard'; import { Authenticated } from 'src/middleware/auth.guard';
import { SystemConfigService } from 'src/services/system-config.service'; import { SystemConfigService } from 'src/services/system-config.service';
@ApiTags('System Config') @ApiTags('System Config')
@Controller('system-config') @Controller('system-config')
@Authenticated({ admin: true })
export class SystemConfigController { export class SystemConfigController {
constructor(private service: SystemConfigService) {} constructor(private service: SystemConfigService) {}
@Get() @Get()
@Authenticated({ admin: true })
getConfig(): Promise<SystemConfigDto> { getConfig(): Promise<SystemConfigDto> {
return this.service.getConfig(); return this.service.getConfig();
} }
@Get('defaults') @Get('defaults')
@Authenticated({ admin: true })
getConfigDefaults(): SystemConfigDto { getConfigDefaults(): SystemConfigDto {
return this.service.getDefaults(); return this.service.getDefaults();
} }
@Put() @Put()
@Authenticated({ admin: true })
updateConfig(@Body() dto: SystemConfigDto): Promise<SystemConfigDto> { updateConfig(@Body() dto: SystemConfigDto): Promise<SystemConfigDto> {
return this.service.updateConfig(dto); return this.service.updateConfig(dto);
} }
@Get('storage-template-options') @Get('storage-template-options')
@Authenticated({ admin: true })
getStorageTemplateOptions(): SystemConfigTemplateStorageOptionDto { getStorageTemplateOptions(): SystemConfigTemplateStorageOptionDto {
return this.service.getStorageTemplateOptions(); return this.service.getStorageTemplateOptions();
} }
@AdminRoute(false) @Authenticated({ sharedLink: true })
@SharedLinkRoute()
@Get('map/style.json') @Get('map/style.json')
getMapStyle(@Query() dto: MapThemeDto) { getMapStyle(@Query() dto: MapThemeDto) {
return this.service.getMapStyle(dto.theme); return this.service.getMapStyle(dto.theme);

View file

@ -6,22 +6,24 @@ import { SystemMetadataService } from 'src/services/system-metadata.service';
@ApiTags('System Metadata') @ApiTags('System Metadata')
@Controller('system-metadata') @Controller('system-metadata')
@Authenticated({ admin: true })
export class SystemMetadataController { export class SystemMetadataController {
constructor(private service: SystemMetadataService) {} constructor(private service: SystemMetadataService) {}
@Get('admin-onboarding') @Get('admin-onboarding')
@Authenticated({ admin: true })
getAdminOnboarding(): Promise<AdminOnboardingUpdateDto> { getAdminOnboarding(): Promise<AdminOnboardingUpdateDto> {
return this.service.getAdminOnboarding(); return this.service.getAdminOnboarding();
} }
@Post('admin-onboarding') @Post('admin-onboarding')
@HttpCode(HttpStatus.NO_CONTENT) @HttpCode(HttpStatus.NO_CONTENT)
@Authenticated({ admin: true })
updateAdminOnboarding(@Body() dto: AdminOnboardingUpdateDto): Promise<void> { updateAdminOnboarding(@Body() dto: AdminOnboardingUpdateDto): Promise<void> {
return this.service.updateAdminOnboarding(dto); return this.service.updateAdminOnboarding(dto);
} }
@Get('reverse-geocoding-state') @Get('reverse-geocoding-state')
@Authenticated({ admin: true })
getReverseGeocodingState(): Promise<ReverseGeocodingStateResponseDto> { getReverseGeocodingState(): Promise<ReverseGeocodingStateResponseDto> {
return this.service.getReverseGeocodingState(); return this.service.getReverseGeocodingState();
} }

View file

@ -11,41 +11,47 @@ import { UUIDParamDto } from 'src/validation';
@ApiTags('Tag') @ApiTags('Tag')
@Controller('tag') @Controller('tag')
@Authenticated()
export class TagController { export class TagController {
constructor(private service: TagService) {} constructor(private service: TagService) {}
@Post() @Post()
@Authenticated()
createTag(@Auth() auth: AuthDto, @Body() dto: CreateTagDto): Promise<TagResponseDto> { createTag(@Auth() auth: AuthDto, @Body() dto: CreateTagDto): Promise<TagResponseDto> {
return this.service.create(auth, dto); return this.service.create(auth, dto);
} }
@Get() @Get()
@Authenticated()
getAllTags(@Auth() auth: AuthDto): Promise<TagResponseDto[]> { getAllTags(@Auth() auth: AuthDto): Promise<TagResponseDto[]> {
return this.service.getAll(auth); return this.service.getAll(auth);
} }
@Get(':id') @Get(':id')
@Authenticated()
getTagById(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<TagResponseDto> { getTagById(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<TagResponseDto> {
return this.service.getById(auth, id); return this.service.getById(auth, id);
} }
@Patch(':id') @Patch(':id')
@Authenticated()
updateTag(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @Body() dto: UpdateTagDto): Promise<TagResponseDto> { updateTag(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @Body() dto: UpdateTagDto): Promise<TagResponseDto> {
return this.service.update(auth, id, dto); return this.service.update(auth, id, dto);
} }
@Delete(':id') @Delete(':id')
@Authenticated()
deleteTag(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<void> { deleteTag(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<void> {
return this.service.remove(auth, id); return this.service.remove(auth, id);
} }
@Get(':id/assets') @Get(':id/assets')
@Authenticated()
getTagAssets(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<AssetResponseDto[]> { getTagAssets(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<AssetResponseDto[]> {
return this.service.getAssets(auth, id); return this.service.getAssets(auth, id);
} }
@Put(':id/assets') @Put(':id/assets')
@Authenticated()
tagAssets( tagAssets(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@Param() { id }: UUIDParamDto, @Param() { id }: UUIDParamDto,
@ -55,6 +61,7 @@ export class TagController {
} }
@Delete(':id/assets') @Delete(':id/assets')
@Authenticated()
untagAssets( untagAssets(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@Body() dto: AssetIdsDto, @Body() dto: AssetIdsDto,

View file

@ -8,17 +8,16 @@ import { TimelineService } from 'src/services/timeline.service';
@ApiTags('Timeline') @ApiTags('Timeline')
@Controller('timeline') @Controller('timeline')
@Authenticated()
export class TimelineController { export class TimelineController {
constructor(private service: TimelineService) {} constructor(private service: TimelineService) {}
@Authenticated({ isShared: true }) @Authenticated({ sharedLink: true })
@Get('buckets') @Get('buckets')
getTimeBuckets(@Auth() auth: AuthDto, @Query() dto: TimeBucketDto): Promise<TimeBucketResponseDto[]> { getTimeBuckets(@Auth() auth: AuthDto, @Query() dto: TimeBucketDto): Promise<TimeBucketResponseDto[]> {
return this.service.getTimeBuckets(auth, dto); return this.service.getTimeBuckets(auth, dto);
} }
@Authenticated({ isShared: true }) @Authenticated({ sharedLink: true })
@Get('bucket') @Get('bucket')
getTimeBucket(@Auth() auth: AuthDto, @Query() dto: TimeBucketAssetDto): Promise<AssetResponseDto[]> { getTimeBucket(@Auth() auth: AuthDto, @Query() dto: TimeBucketAssetDto): Promise<AssetResponseDto[]> {
return this.service.getTimeBucket(auth, dto) as Promise<AssetResponseDto[]>; return this.service.getTimeBucket(auth, dto) as Promise<AssetResponseDto[]>;

View file

@ -7,24 +7,26 @@ import { TrashService } from 'src/services/trash.service';
@ApiTags('Trash') @ApiTags('Trash')
@Controller('trash') @Controller('trash')
@Authenticated()
export class TrashController { export class TrashController {
constructor(private service: TrashService) {} constructor(private service: TrashService) {}
@Post('empty') @Post('empty')
@HttpCode(HttpStatus.NO_CONTENT) @HttpCode(HttpStatus.NO_CONTENT)
@Authenticated()
emptyTrash(@Auth() auth: AuthDto): Promise<void> { emptyTrash(@Auth() auth: AuthDto): Promise<void> {
return this.service.empty(auth); return this.service.empty(auth);
} }
@Post('restore') @Post('restore')
@HttpCode(HttpStatus.NO_CONTENT) @HttpCode(HttpStatus.NO_CONTENT)
@Authenticated()
restoreTrash(@Auth() auth: AuthDto): Promise<void> { restoreTrash(@Auth() auth: AuthDto): Promise<void> {
return this.service.restore(auth); return this.service.restore(auth);
} }
@Post('restore/assets') @Post('restore/assets')
@HttpCode(HttpStatus.NO_CONTENT) @HttpCode(HttpStatus.NO_CONTENT)
@Authenticated()
restoreAssets(@Auth() auth: AuthDto, @Body() dto: BulkIdsDto): Promise<void> { restoreAssets(@Auth() auth: AuthDto, @Body() dto: BulkIdsDto): Promise<void> {
return this.service.restoreAssets(auth, dto); return this.service.restoreAssets(auth, dto);
} }

View file

@ -19,7 +19,7 @@ import { NextFunction, Response } from 'express';
import { AuthDto } from 'src/dtos/auth.dto'; import { AuthDto } from 'src/dtos/auth.dto';
import { CreateProfileImageDto, CreateProfileImageResponseDto } from 'src/dtos/user-profile.dto'; import { CreateProfileImageDto, CreateProfileImageResponseDto } from 'src/dtos/user-profile.dto';
import { CreateUserDto, DeleteUserDto, UpdateUserDto, UserResponseDto } from 'src/dtos/user.dto'; import { CreateUserDto, DeleteUserDto, UpdateUserDto, UserResponseDto } from 'src/dtos/user.dto';
import { AdminRoute, Auth, Authenticated, FileResponse } from 'src/middleware/auth.guard'; import { Auth, Authenticated, FileResponse } from 'src/middleware/auth.guard';
import { FileUploadInterceptor, Route } from 'src/middleware/file-upload.interceptor'; import { FileUploadInterceptor, Route } from 'src/middleware/file-upload.interceptor';
import { UserService } from 'src/services/user.service'; import { UserService } from 'src/services/user.service';
import { sendFile } from 'src/utils/file'; import { sendFile } from 'src/utils/file';
@ -27,39 +27,42 @@ import { UUIDParamDto } from 'src/validation';
@ApiTags('User') @ApiTags('User')
@Controller(Route.USER) @Controller(Route.USER)
@Authenticated()
export class UserController { export class UserController {
constructor(private service: UserService) {} constructor(private service: UserService) {}
@Get() @Get()
@Authenticated()
getAllUsers(@Auth() auth: AuthDto, @Query('isAll') isAll: boolean): Promise<UserResponseDto[]> { getAllUsers(@Auth() auth: AuthDto, @Query('isAll') isAll: boolean): Promise<UserResponseDto[]> {
return this.service.getAll(auth, isAll); return this.service.getAll(auth, isAll);
} }
@Get('info/:id') @Get('info/:id')
@Authenticated()
getUserById(@Param() { id }: UUIDParamDto): Promise<UserResponseDto> { getUserById(@Param() { id }: UUIDParamDto): Promise<UserResponseDto> {
return this.service.get(id); return this.service.get(id);
} }
@Get('me') @Get('me')
@Authenticated()
getMyUserInfo(@Auth() auth: AuthDto): Promise<UserResponseDto> { getMyUserInfo(@Auth() auth: AuthDto): Promise<UserResponseDto> {
return this.service.getMe(auth); return this.service.getMe(auth);
} }
@AdminRoute()
@Post() @Post()
@Authenticated({ admin: true })
createUser(@Body() createUserDto: CreateUserDto): Promise<UserResponseDto> { createUser(@Body() createUserDto: CreateUserDto): Promise<UserResponseDto> {
return this.service.create(createUserDto); return this.service.create(createUserDto);
} }
@Delete('profile-image') @Delete('profile-image')
@HttpCode(HttpStatus.NO_CONTENT) @HttpCode(HttpStatus.NO_CONTENT)
@Authenticated()
deleteProfileImage(@Auth() auth: AuthDto): Promise<void> { deleteProfileImage(@Auth() auth: AuthDto): Promise<void> {
return this.service.deleteProfileImage(auth); return this.service.deleteProfileImage(auth);
} }
@AdminRoute()
@Delete(':id') @Delete(':id')
@Authenticated({ admin: true })
deleteUser( deleteUser(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@Param() { id }: UUIDParamDto, @Param() { id }: UUIDParamDto,
@ -68,14 +71,15 @@ export class UserController {
return this.service.delete(auth, id, dto); return this.service.delete(auth, id, dto);
} }
@AdminRoute()
@Post(':id/restore') @Post(':id/restore')
@Authenticated({ admin: true })
restoreUser(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<UserResponseDto> { restoreUser(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<UserResponseDto> {
return this.service.restore(auth, id); return this.service.restore(auth, id);
} }
// TODO: replace with @Put(':id') // TODO: replace with @Put(':id')
@Put() @Put()
@Authenticated()
updateUser(@Auth() auth: AuthDto, @Body() updateUserDto: UpdateUserDto): Promise<UserResponseDto> { updateUser(@Auth() auth: AuthDto, @Body() updateUserDto: UpdateUserDto): Promise<UserResponseDto> {
return this.service.update(auth, updateUserDto); return this.service.update(auth, updateUserDto);
} }
@ -84,6 +88,7 @@ export class UserController {
@ApiConsumes('multipart/form-data') @ApiConsumes('multipart/form-data')
@ApiBody({ description: 'A new avatar for the user', type: CreateProfileImageDto }) @ApiBody({ description: 'A new avatar for the user', type: CreateProfileImageDto })
@Post('profile-image') @Post('profile-image')
@Authenticated()
createProfileImage( createProfileImage(
@Auth() auth: AuthDto, @Auth() auth: AuthDto,
@UploadedFile() fileInfo: Express.Multer.File, @UploadedFile() fileInfo: Express.Multer.File,
@ -93,6 +98,7 @@ export class UserController {
@Get('profile-image/:id') @Get('profile-image/:id')
@FileResponse() @FileResponse()
@Authenticated()
async getProfileImage(@Res() res: Response, @Next() next: NextFunction, @Param() { id }: UUIDParamDto) { async getProfileImage(@Res() res: Response, @Next() next: NextFunction, @Param() { id }: UUIDParamDto) {
await sendFile(res, next, () => this.service.getProfileImage(id)); await sendFile(res, next, () => this.service.getProfileImage(id));
} }

View file

@ -17,10 +17,14 @@ export enum ImmichHeader {
API_KEY = 'x-api-key', API_KEY = 'x-api-key',
USER_TOKEN = 'x-immich-user-token', USER_TOKEN = 'x-immich-user-token',
SESSION_TOKEN = 'x-immich-session-token', SESSION_TOKEN = 'x-immich-session-token',
SHARED_LINK_TOKEN = 'x-immich-share-key', SHARED_LINK_KEY = 'x-immich-share-key',
CHECKSUM = 'x-immich-checksum', CHECKSUM = 'x-immich-checksum',
} }
export enum ImmichQuery {
SHARED_LINK_KEY = 'key',
}
export type CookieResponse = { export type CookieResponse = {
isSecure: boolean; isSecure: boolean;
values: Array<{ key: ImmichCookie; value: string }>; values: Array<{ key: ImmichCookie; value: string }>;

View file

@ -10,7 +10,7 @@ import {
import { Reflector } from '@nestjs/core'; import { Reflector } from '@nestjs/core';
import { ApiBearerAuth, ApiCookieAuth, ApiOkResponse, ApiQuery, ApiSecurity } from '@nestjs/swagger'; import { ApiBearerAuth, ApiCookieAuth, ApiOkResponse, ApiQuery, ApiSecurity } from '@nestjs/swagger';
import { Request } from 'express'; import { Request } from 'express';
import { AuthDto } from 'src/dtos/auth.dto'; import { AuthDto, ImmichQuery } from 'src/dtos/auth.dto';
import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface';
import { AuthService, LoginDetails } from 'src/services/auth.service'; import { AuthService, LoginDetails } from 'src/services/auth.service';
import { UAParser } from 'ua-parser-js'; import { UAParser } from 'ua-parser-js';
@ -19,42 +19,30 @@ 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',
API_KEY_SECURITY = 'api_key', API_KEY_SECURITY = 'api_key',
} }
export interface AuthenticatedOptions { type AdminRoute = { admin?: true };
admin?: true; type SharedLinkRoute = { sharedLink?: true };
isShared?: true; type AuthenticatedOptions = AdminRoute | SharedLinkRoute;
}
export const Authenticated = (options: AuthenticatedOptions = {}) => { export const Authenticated = (options?: AuthenticatedOptions): MethodDecorator => {
const decorators: MethodDecorator[] = [ const decorators: MethodDecorator[] = [
ApiBearerAuth(), ApiBearerAuth(),
ApiCookieAuth(), ApiCookieAuth(),
ApiSecurity(Metadata.API_KEY_SECURITY), ApiSecurity(Metadata.API_KEY_SECURITY),
SetMetadata(Metadata.AUTH_ROUTE, true), SetMetadata(Metadata.AUTH_ROUTE, options || {}),
]; ];
if (options.admin) { if ((options as SharedLinkRoute)?.sharedLink) {
decorators.push(AdminRoute()); decorators.push(ApiQuery({ name: ImmichQuery.SHARED_LINK_KEY, type: String, required: false }));
}
if (options.isShared) {
decorators.push(SharedLinkRoute());
} }
return applyDecorators(...decorators); return applyDecorators(...decorators);
}; };
export const PublicRoute = () =>
applyDecorators(SetMetadata(Metadata.AUTH_ROUTE, false), ApiSecurity(Metadata.PUBLIC_SECURITY));
export const SharedLinkRoute = () =>
applyDecorators(SetMetadata(Metadata.SHARED_ROUTE, true), ApiQuery({ name: 'key', type: String, required: false }));
export const AdminRoute = (value = true) => SetMetadata(Metadata.ADMIN_ROUTE, value);
export const Auth = createParamDecorator((data, context: ExecutionContext): AuthDto => { export const Auth = createParamDecorator((data, context: ExecutionContext): AuthDto => {
return context.switchToHttp().getRequest<{ user: AuthDto }>().user; return context.switchToHttp().getRequest<AuthenticatedRequest>().user;
}); });
export const FileResponse = () => export const FileResponse = () =>
@ -93,25 +81,22 @@ export class AuthGuard implements CanActivate {
} }
async canActivate(context: ExecutionContext): Promise<boolean> { async canActivate(context: ExecutionContext): Promise<boolean> {
const targets = [context.getHandler(), context.getClass()]; const targets = [context.getHandler()];
const isAuthRoute = this.reflector.getAllAndOverride(Metadata.AUTH_ROUTE, targets); const options = this.reflector.getAllAndOverride<AuthenticatedOptions | undefined>(Metadata.AUTH_ROUTE, targets);
const isAdminRoute = this.reflector.getAllAndOverride(Metadata.ADMIN_ROUTE, targets); if (!options) {
const isSharedRoute = this.reflector.getAllAndOverride(Metadata.SHARED_ROUTE, targets);
if (!isAuthRoute) {
return true; return true;
} }
const request = context.switchToHttp().getRequest<AuthRequest>(); const request = context.switchToHttp().getRequest<AuthRequest>();
const authDto = await this.authService.validate(request.headers, request.query as Record<string, string>); const authDto = await this.authService.validate(request.headers, request.query as Record<string, string>);
if (authDto.sharedLink && !isSharedRoute) { if (authDto.sharedLink && !(options as SharedLinkRoute).sharedLink) {
this.logger.warn(`Denied access to non-shared route: ${request.path}`); this.logger.warn(`Denied access to non-shared route: ${request.path}`);
return false; return false;
} }
if (isAdminRoute && !authDto.user.isAdmin) { if (!authDto.user.isAdmin && (options as AdminRoute).admin) {
this.logger.warn(`Denied access to admin only route: ${request.path}`); this.logger.warn(`Denied access to admin only route: ${request.path}`);
return false; return false;
} }

View file

@ -149,7 +149,7 @@ export class AuthService {
} }
async validate(headers: IncomingHttpHeaders, params: Record<string, string>): Promise<AuthDto> { async validate(headers: IncomingHttpHeaders, params: Record<string, string>): Promise<AuthDto> {
const shareKey = (headers[ImmichHeader.SHARED_LINK_TOKEN] || params.key) as string; const shareKey = (headers[ImmichHeader.SHARED_LINK_KEY] || params.key) as string;
const session = (headers[ImmichHeader.USER_TOKEN] || const session = (headers[ImmichHeader.USER_TOKEN] ||
headers[ImmichHeader.SESSION_TOKEN] || headers[ImmichHeader.SESSION_TOKEN] ||
params.sessionKey || params.sessionKey ||

View file

@ -103,10 +103,6 @@ const patchOpenAPI = (document: OpenAPIObject) => {
continue; continue;
} }
if ((operation.security || []).some((item) => !!item[Metadata.PUBLIC_SECURITY])) {
delete operation.security;
}
if (operation.summary === '') { if (operation.summary === '') {
delete operation.summary; delete operation.summary;
} }