mirror of
https://github.com/immich-app/immich.git
synced 2025-12-24 01:11:32 +03:00
@@ -67,7 +67,7 @@ export class AlbumController {
|
||||
}
|
||||
|
||||
@Put(':id/assets')
|
||||
@Authenticated({ sharedLink: true })
|
||||
@Authenticated({ permission: Permission.AlbumAssetCreate, sharedLink: true })
|
||||
addAssetsToAlbum(
|
||||
@Auth() auth: AuthDto,
|
||||
@Param() { id }: UUIDParamDto,
|
||||
@@ -77,7 +77,7 @@ export class AlbumController {
|
||||
}
|
||||
|
||||
@Delete(':id/assets')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.AlbumAssetDelete })
|
||||
removeAssetFromAlbum(
|
||||
@Auth() auth: AuthDto,
|
||||
@Body() dto: BulkIdsDto,
|
||||
@@ -87,7 +87,7 @@ export class AlbumController {
|
||||
}
|
||||
|
||||
@Put(':id/users')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.AlbumUserCreate })
|
||||
addUsersToAlbum(
|
||||
@Auth() auth: AuthDto,
|
||||
@Param() { id }: UUIDParamDto,
|
||||
@@ -97,7 +97,7 @@ export class AlbumController {
|
||||
}
|
||||
|
||||
@Put(':id/user/:userId')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.AlbumUserUpdate })
|
||||
updateAlbumUser(
|
||||
@Auth() auth: AuthDto,
|
||||
@Param() { id }: UUIDParamDto,
|
||||
@@ -108,7 +108,7 @@ export class AlbumController {
|
||||
}
|
||||
|
||||
@Delete(':id/user/:userId')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.AlbumUserDelete })
|
||||
removeUserFromAlbum(
|
||||
@Auth() auth: AuthDto,
|
||||
@Param() { id }: UUIDParamDto,
|
||||
|
||||
@@ -34,7 +34,7 @@ import {
|
||||
UploadFieldName,
|
||||
} from 'src/dtos/asset-media.dto';
|
||||
import { AuthDto } from 'src/dtos/auth.dto';
|
||||
import { ImmichHeader, RouteKey } from 'src/enum';
|
||||
import { ImmichHeader, Permission, RouteKey } from 'src/enum';
|
||||
import { AssetUploadInterceptor } from 'src/middleware/asset-upload.interceptor';
|
||||
import { Auth, Authenticated, FileResponse } from 'src/middleware/auth.guard';
|
||||
import { FileUploadInterceptor, getFiles } from 'src/middleware/file-upload.interceptor';
|
||||
@@ -61,7 +61,7 @@ export class AssetMediaController {
|
||||
required: false,
|
||||
})
|
||||
@ApiBody({ description: 'Asset Upload Information', type: AssetMediaCreateDto })
|
||||
@Authenticated({ sharedLink: true })
|
||||
@Authenticated({ permission: Permission.AssetUpload, sharedLink: true })
|
||||
async uploadAsset(
|
||||
@Auth() auth: AuthDto,
|
||||
@UploadedFiles(new ParseFilePipe({ validators: [new FileNotEmptyValidator(['assetData'])] })) files: UploadFiles,
|
||||
@@ -80,7 +80,7 @@ export class AssetMediaController {
|
||||
|
||||
@Get(':id/original')
|
||||
@FileResponse()
|
||||
@Authenticated({ sharedLink: true })
|
||||
@Authenticated({ permission: Permission.AssetDownload, sharedLink: true })
|
||||
async downloadAsset(
|
||||
@Auth() auth: AuthDto,
|
||||
@Param() { id }: UUIDParamDto,
|
||||
@@ -101,7 +101,7 @@ export class AssetMediaController {
|
||||
summary: 'replaceAsset',
|
||||
description: 'Replace the asset with new file, without changing its id',
|
||||
})
|
||||
@Authenticated({ sharedLink: true })
|
||||
@Authenticated({ permission: Permission.AssetReplace, sharedLink: true })
|
||||
async replaceAsset(
|
||||
@Auth() auth: AuthDto,
|
||||
@Param() { id }: UUIDParamDto,
|
||||
@@ -120,7 +120,7 @@ export class AssetMediaController {
|
||||
|
||||
@Get(':id/thumbnail')
|
||||
@FileResponse()
|
||||
@Authenticated({ sharedLink: true })
|
||||
@Authenticated({ permission: Permission.AssetView, sharedLink: true })
|
||||
async viewAsset(
|
||||
@Auth() auth: AuthDto,
|
||||
@Param() { id }: UUIDParamDto,
|
||||
@@ -157,7 +157,7 @@ export class AssetMediaController {
|
||||
|
||||
@Get(':id/video/playback')
|
||||
@FileResponse()
|
||||
@Authenticated({ sharedLink: true })
|
||||
@Authenticated({ permission: Permission.AssetView, sharedLink: true })
|
||||
async playAssetVideo(
|
||||
@Auth() auth: AuthDto,
|
||||
@Param() { id }: UUIDParamDto,
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
UpdateAssetDto,
|
||||
} from 'src/dtos/asset.dto';
|
||||
import { AuthDto } from 'src/dtos/auth.dto';
|
||||
import { RouteKey } from 'src/enum';
|
||||
import { Permission, RouteKey } from 'src/enum';
|
||||
import { Auth, Authenticated } from 'src/middleware/auth.guard';
|
||||
import { AssetService } from 'src/services/asset.service';
|
||||
import { UUIDParamDto } from 'src/validation';
|
||||
@@ -24,7 +24,7 @@ export class AssetController {
|
||||
constructor(private service: AssetService) {}
|
||||
|
||||
@Get('random')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.AssetRead })
|
||||
@EndpointLifecycle({ deprecatedAt: 'v1.116.0' })
|
||||
getRandom(@Auth() auth: AuthDto, @Query() dto: RandomAssetsDto): Promise<AssetResponseDto[]> {
|
||||
return this.service.getRandom(auth, dto.count ?? 1);
|
||||
@@ -44,7 +44,7 @@ export class AssetController {
|
||||
}
|
||||
|
||||
@Get('statistics')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.AssetStatistics })
|
||||
getAssetStatistics(@Auth() auth: AuthDto, @Query() dto: AssetStatsDto): Promise<AssetStatsResponseDto> {
|
||||
return this.service.getStatistics(auth, dto);
|
||||
}
|
||||
@@ -58,26 +58,26 @@ export class AssetController {
|
||||
|
||||
@Put()
|
||||
@HttpCode(HttpStatus.NO_CONTENT)
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.AssetUpdate })
|
||||
updateAssets(@Auth() auth: AuthDto, @Body() dto: AssetBulkUpdateDto): Promise<void> {
|
||||
return this.service.updateAll(auth, dto);
|
||||
}
|
||||
|
||||
@Delete()
|
||||
@HttpCode(HttpStatus.NO_CONTENT)
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.AssetDelete })
|
||||
deleteAssets(@Auth() auth: AuthDto, @Body() dto: AssetBulkDeleteDto): Promise<void> {
|
||||
return this.service.deleteAll(auth, dto);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@Authenticated({ sharedLink: true })
|
||||
@Authenticated({ permission: Permission.AssetRead, sharedLink: true })
|
||||
getAssetInfo(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<AssetResponseDto> {
|
||||
return this.service.get(auth, id) as Promise<AssetResponseDto>;
|
||||
}
|
||||
|
||||
@Put(':id')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.AssetUpdate })
|
||||
updateAsset(
|
||||
@Auth() auth: AuthDto,
|
||||
@Param() { id }: UUIDParamDto,
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
ValidateAccessTokenResponseDto,
|
||||
} from 'src/dtos/auth.dto';
|
||||
import { UserAdminResponseDto } from 'src/dtos/user.dto';
|
||||
import { AuthType, ImmichCookie } from 'src/enum';
|
||||
import { AuthType, ImmichCookie, Permission } from 'src/enum';
|
||||
import { Auth, Authenticated, GetLoginDetails } from 'src/middleware/auth.guard';
|
||||
import { AuthService, LoginDetails } from 'src/services/auth.service';
|
||||
import { respondWithCookie, respondWithoutCookie } from 'src/utils/response';
|
||||
@@ -57,7 +57,7 @@ export class AuthController {
|
||||
|
||||
@Post('change-password')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.AuthChangePassword })
|
||||
changePassword(@Auth() auth: AuthDto, @Body() dto: ChangePasswordDto): Promise<UserAdminResponseDto> {
|
||||
return this.service.changePassword(auth, dto);
|
||||
}
|
||||
@@ -87,19 +87,19 @@ export class AuthController {
|
||||
}
|
||||
|
||||
@Post('pin-code')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.PinCodeCreate })
|
||||
setupPinCode(@Auth() auth: AuthDto, @Body() dto: PinCodeSetupDto): Promise<void> {
|
||||
return this.service.setupPinCode(auth, dto);
|
||||
}
|
||||
|
||||
@Put('pin-code')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.PinCodeUpdate })
|
||||
async changePinCode(@Auth() auth: AuthDto, @Body() dto: PinCodeChangeDto): Promise<void> {
|
||||
return this.service.changePinCode(auth, dto);
|
||||
}
|
||||
|
||||
@Delete('pin-code')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.PinCodeDelete })
|
||||
async resetPinCode(@Auth() auth: AuthDto, @Body() dto: PinCodeResetDto): Promise<void> {
|
||||
return this.service.resetPinCode(auth, dto);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ApiTags } from '@nestjs/swagger';
|
||||
import { AssetIdsDto } from 'src/dtos/asset.dto';
|
||||
import { AuthDto } from 'src/dtos/auth.dto';
|
||||
import { DownloadInfoDto, DownloadResponseDto } from 'src/dtos/download.dto';
|
||||
import { Permission } from 'src/enum';
|
||||
import { Auth, Authenticated, FileResponse } from 'src/middleware/auth.guard';
|
||||
import { DownloadService } from 'src/services/download.service';
|
||||
import { asStreamableFile } from 'src/utils/file';
|
||||
@@ -13,7 +14,7 @@ export class DownloadController {
|
||||
constructor(private service: DownloadService) {}
|
||||
|
||||
@Post('info')
|
||||
@Authenticated({ sharedLink: true })
|
||||
@Authenticated({ permission: Permission.AssetDownload, sharedLink: true })
|
||||
getDownloadInfo(@Auth() auth: AuthDto, @Body() dto: DownloadInfoDto): Promise<DownloadResponseDto> {
|
||||
return this.service.getDownloadInfo(auth, dto);
|
||||
}
|
||||
@@ -21,7 +22,7 @@ export class DownloadController {
|
||||
@Post('archive')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@FileResponse()
|
||||
@Authenticated({ sharedLink: true })
|
||||
@Authenticated({ permission: Permission.AssetDownload, sharedLink: true })
|
||||
downloadArchive(@Auth() auth: AuthDto, @Body() dto: AssetIdsDto): Promise<StreamableFile> {
|
||||
return this.service.downloadArchive(auth, dto).then(asStreamableFile);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ApiTags } from '@nestjs/swagger';
|
||||
import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
|
||||
import { AuthDto } from 'src/dtos/auth.dto';
|
||||
import { DuplicateResponseDto } from 'src/dtos/duplicate.dto';
|
||||
import { Permission } from 'src/enum';
|
||||
import { Auth, Authenticated } from 'src/middleware/auth.guard';
|
||||
import { DuplicateService } from 'src/services/duplicate.service';
|
||||
import { UUIDParamDto } from 'src/validation';
|
||||
@@ -13,19 +14,19 @@ export class DuplicateController {
|
||||
constructor(private service: DuplicateService) {}
|
||||
|
||||
@Get()
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.DuplicateRead })
|
||||
getAssetDuplicates(@Auth() auth: AuthDto): Promise<DuplicateResponseDto[]> {
|
||||
return this.service.getDuplicates(auth);
|
||||
}
|
||||
|
||||
@Delete()
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.DuplicateDelete })
|
||||
deleteDuplicates(@Auth() auth: AuthDto, @Body() dto: BulkIdsDto): Promise<void> {
|
||||
return this.service.deleteAll(auth, dto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.DuplicateDelete })
|
||||
deleteDuplicate(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<void> {
|
||||
return this.service.delete(auth, id);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Body, Controller, Get, Param, Post, Put } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { AllJobStatusResponseDto, JobCommandDto, JobCreateDto, JobIdParamDto, JobStatusDto } from 'src/dtos/job.dto';
|
||||
import { Permission } from 'src/enum';
|
||||
import { Authenticated } from 'src/middleware/auth.guard';
|
||||
import { JobService } from 'src/services/job.service';
|
||||
|
||||
@@ -10,19 +11,19 @@ export class JobController {
|
||||
constructor(private service: JobService) {}
|
||||
|
||||
@Get()
|
||||
@Authenticated({ admin: true })
|
||||
@Authenticated({ permission: Permission.JobRead, admin: true })
|
||||
getAllJobsStatus(): Promise<AllJobStatusResponseDto> {
|
||||
return this.service.getAllJobsStatus();
|
||||
}
|
||||
|
||||
@Post()
|
||||
@Authenticated({ admin: true })
|
||||
@Authenticated({ permission: Permission.JobCreate, admin: true })
|
||||
createJob(@Body() dto: JobCreateDto): Promise<void> {
|
||||
return this.service.create(dto);
|
||||
}
|
||||
|
||||
@Put(':id')
|
||||
@Authenticated({ admin: true })
|
||||
@Authenticated({ permission: Permission.JobCreate, admin: true })
|
||||
sendJobCommand(@Param() { id }: JobIdParamDto, @Body() dto: JobCommandDto): Promise<JobStatusDto> {
|
||||
return this.service.handleCommand(id, dto);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ export class MemoryController {
|
||||
}
|
||||
|
||||
@Get('statistics')
|
||||
@Authenticated({ permission: Permission.MemoryRead })
|
||||
@Authenticated({ permission: Permission.MemoryStatistics })
|
||||
memoriesStatistics(@Auth() auth: AuthDto, @Query() dto: MemorySearchDto): Promise<MemoryStatisticsResponseDto> {
|
||||
return this.service.statistics(auth, dto);
|
||||
}
|
||||
@@ -61,7 +61,7 @@ export class MemoryController {
|
||||
}
|
||||
|
||||
@Put(':id/assets')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.MemoryAssetCreate })
|
||||
addMemoryAssets(
|
||||
@Auth() auth: AuthDto,
|
||||
@Param() { id }: UUIDParamDto,
|
||||
@@ -72,7 +72,7 @@ export class MemoryController {
|
||||
|
||||
@Delete(':id/assets')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.MemoryAssetDelete })
|
||||
removeMemoryAssets(
|
||||
@Auth() auth: AuthDto,
|
||||
@Body() dto: BulkIdsDto,
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
SmartSearchDto,
|
||||
StatisticsSearchDto,
|
||||
} from 'src/dtos/search.dto';
|
||||
import { Permission } from 'src/enum';
|
||||
import { Auth, Authenticated } from 'src/middleware/auth.guard';
|
||||
import { SearchService } from 'src/services/search.service';
|
||||
|
||||
@@ -26,58 +27,58 @@ export class SearchController {
|
||||
|
||||
@Post('metadata')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.AssetRead })
|
||||
searchAssets(@Auth() auth: AuthDto, @Body() dto: MetadataSearchDto): Promise<SearchResponseDto> {
|
||||
return this.service.searchMetadata(auth, dto);
|
||||
}
|
||||
|
||||
@Post('statistics')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.AssetStatistics })
|
||||
searchAssetStatistics(@Auth() auth: AuthDto, @Body() dto: StatisticsSearchDto): Promise<SearchStatisticsResponseDto> {
|
||||
return this.service.searchStatistics(auth, dto);
|
||||
}
|
||||
|
||||
@Post('random')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.AssetRead })
|
||||
searchRandom(@Auth() auth: AuthDto, @Body() dto: RandomSearchDto): Promise<AssetResponseDto[]> {
|
||||
return this.service.searchRandom(auth, dto);
|
||||
}
|
||||
|
||||
@Post('smart')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.AssetRead })
|
||||
searchSmart(@Auth() auth: AuthDto, @Body() dto: SmartSearchDto): Promise<SearchResponseDto> {
|
||||
return this.service.searchSmart(auth, dto);
|
||||
}
|
||||
|
||||
@Get('explore')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.AssetRead })
|
||||
getExploreData(@Auth() auth: AuthDto): Promise<SearchExploreResponseDto[]> {
|
||||
return this.service.getExploreData(auth);
|
||||
}
|
||||
|
||||
@Get('person')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.PersonRead })
|
||||
searchPerson(@Auth() auth: AuthDto, @Query() dto: SearchPeopleDto): Promise<PersonResponseDto[]> {
|
||||
return this.service.searchPerson(auth, dto);
|
||||
}
|
||||
|
||||
@Get('places')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.AssetRead })
|
||||
searchPlaces(@Query() dto: SearchPlacesDto): Promise<PlacesResponseDto[]> {
|
||||
return this.service.searchPlaces(dto);
|
||||
}
|
||||
|
||||
@Get('cities')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.AssetRead })
|
||||
getAssetsByCity(@Auth() auth: AuthDto): Promise<AssetResponseDto[]> {
|
||||
return this.service.getAssetsByCity(auth);
|
||||
}
|
||||
|
||||
@Get('suggestions')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.AssetRead })
|
||||
getSearchSuggestions(@Auth() auth: AuthDto, @Query() dto: SearchSuggestionRequestDto): Promise<string[]> {
|
||||
// TODO fix open api generation to indicate that results can be nullable
|
||||
return this.service.getSearchSuggestions(auth, dto) as Promise<string[]>;
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
ServerVersionResponseDto,
|
||||
} from 'src/dtos/server.dto';
|
||||
import { VersionCheckStateResponseDto } from 'src/dtos/system-metadata.dto';
|
||||
import { Permission } from 'src/enum';
|
||||
import { Authenticated } from 'src/middleware/auth.guard';
|
||||
import { ServerService } from 'src/services/server.service';
|
||||
import { SystemMetadataService } from 'src/services/system-metadata.service';
|
||||
@@ -30,19 +31,19 @@ export class ServerController {
|
||||
) {}
|
||||
|
||||
@Get('about')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.ServerAbout })
|
||||
getAboutInfo(): Promise<ServerAboutResponseDto> {
|
||||
return this.service.getAboutInfo();
|
||||
}
|
||||
|
||||
@Get('apk-links')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.ServerApkLinks })
|
||||
getApkLinks(): ServerApkLinksDto {
|
||||
return this.service.getApkLinks();
|
||||
}
|
||||
|
||||
@Get('storage')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.ServerStorage })
|
||||
getStorage(): Promise<ServerStorageResponseDto> {
|
||||
return this.service.getStorage();
|
||||
}
|
||||
@@ -78,7 +79,7 @@ export class ServerController {
|
||||
}
|
||||
|
||||
@Get('statistics')
|
||||
@Authenticated({ admin: true })
|
||||
@Authenticated({ permission: Permission.ServerStatistics, admin: true })
|
||||
getServerStatistics(): Promise<ServerStatsResponseDto> {
|
||||
return this.service.getStatistics();
|
||||
}
|
||||
@@ -88,25 +89,25 @@ export class ServerController {
|
||||
return this.service.getSupportedMediaTypes();
|
||||
}
|
||||
|
||||
@Get('license')
|
||||
@Authenticated({ permission: Permission.ServerLicenseRead, admin: true })
|
||||
@ApiNotFoundResponse()
|
||||
getServerLicense(): Promise<LicenseResponseDto> {
|
||||
return this.service.getLicense();
|
||||
}
|
||||
|
||||
@Put('license')
|
||||
@Authenticated({ admin: true })
|
||||
@Authenticated({ permission: Permission.ServerLicenseUpdate, admin: true })
|
||||
setServerLicense(@Body() license: LicenseKeyDto): Promise<LicenseResponseDto> {
|
||||
return this.service.setLicense(license);
|
||||
}
|
||||
|
||||
@Delete('license')
|
||||
@Authenticated({ admin: true })
|
||||
@Authenticated({ permission: Permission.ServerLicenseDelete, admin: true })
|
||||
deleteServerLicense(): Promise<void> {
|
||||
return this.service.deleteLicense();
|
||||
}
|
||||
|
||||
@Get('license')
|
||||
@Authenticated({ admin: true })
|
||||
@ApiNotFoundResponse()
|
||||
getServerLicense(): Promise<LicenseResponseDto> {
|
||||
return this.service.getLicense();
|
||||
}
|
||||
|
||||
@Get('version-check')
|
||||
@Authenticated()
|
||||
getVersionCheck(): Promise<VersionCheckStateResponseDto> {
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
SyncAckSetDto,
|
||||
SyncStreamDto,
|
||||
} from 'src/dtos/sync.dto';
|
||||
import { Permission } from 'src/enum';
|
||||
import { Auth, Authenticated } from 'src/middleware/auth.guard';
|
||||
import { GlobalExceptionFilter } from 'src/middleware/global-exception.filter';
|
||||
import { SyncService } from 'src/services/sync.service';
|
||||
@@ -41,7 +42,7 @@ export class SyncController {
|
||||
@Post('stream')
|
||||
@Header('Content-Type', 'application/jsonlines+json')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.SyncStream })
|
||||
async getSyncStream(@Auth() auth: AuthDto, @Res() res: Response, @Body() dto: SyncStreamDto) {
|
||||
try {
|
||||
await this.service.stream(auth, res, dto);
|
||||
@@ -52,21 +53,21 @@ export class SyncController {
|
||||
}
|
||||
|
||||
@Get('ack')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.SyncCheckpointRead })
|
||||
getSyncAck(@Auth() auth: AuthDto): Promise<SyncAckDto[]> {
|
||||
return this.service.getAcks(auth);
|
||||
}
|
||||
|
||||
@Post('ack')
|
||||
@HttpCode(HttpStatus.NO_CONTENT)
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.SyncCheckpointUpdate })
|
||||
sendSyncAck(@Auth() auth: AuthDto, @Body() dto: SyncAckSetDto) {
|
||||
return this.service.setAcks(auth, dto);
|
||||
}
|
||||
|
||||
@Delete('ack')
|
||||
@HttpCode(HttpStatus.NO_CONTENT)
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.SyncCheckpointDelete })
|
||||
deleteSyncAck(@Auth() auth: AuthDto, @Body() dto: SyncAckDeleteDto) {
|
||||
return this.service.deleteAcks(auth, dto);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import { OnboardingDto, OnboardingResponseDto } from 'src/dtos/onboarding.dto';
|
||||
import { UserPreferencesResponseDto, UserPreferencesUpdateDto } from 'src/dtos/user-preferences.dto';
|
||||
import { CreateProfileImageDto, CreateProfileImageResponseDto } from 'src/dtos/user-profile.dto';
|
||||
import { UserAdminResponseDto, UserResponseDto, UserUpdateMeDto } from 'src/dtos/user.dto';
|
||||
import { RouteKey } from 'src/enum';
|
||||
import { Permission, RouteKey } from 'src/enum';
|
||||
import { Auth, Authenticated, FileResponse } from 'src/middleware/auth.guard';
|
||||
import { FileUploadInterceptor } from 'src/middleware/file-upload.interceptor';
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
@@ -38,31 +38,31 @@ export class UserController {
|
||||
) {}
|
||||
|
||||
@Get()
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.UserRead })
|
||||
searchUsers(@Auth() auth: AuthDto): Promise<UserResponseDto[]> {
|
||||
return this.service.search(auth);
|
||||
}
|
||||
|
||||
@Get('me')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.UserRead })
|
||||
getMyUser(@Auth() auth: AuthDto): Promise<UserAdminResponseDto> {
|
||||
return this.service.getMe(auth);
|
||||
}
|
||||
|
||||
@Put('me')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.UserUpdate })
|
||||
updateMyUser(@Auth() auth: AuthDto, @Body() dto: UserUpdateMeDto): Promise<UserAdminResponseDto> {
|
||||
return this.service.updateMe(auth, dto);
|
||||
}
|
||||
|
||||
@Get('me/preferences')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.UserPreferenceRead })
|
||||
getMyPreferences(@Auth() auth: AuthDto): Promise<UserPreferencesResponseDto> {
|
||||
return this.service.getMyPreferences(auth);
|
||||
}
|
||||
|
||||
@Put('me/preferences')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.UserPreferenceUpdate })
|
||||
updateMyPreferences(
|
||||
@Auth() auth: AuthDto,
|
||||
@Body() dto: UserPreferencesUpdateDto,
|
||||
@@ -71,43 +71,43 @@ export class UserController {
|
||||
}
|
||||
|
||||
@Get('me/license')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.UserLicenseRead })
|
||||
getUserLicense(@Auth() auth: AuthDto): Promise<LicenseResponseDto> {
|
||||
return this.service.getLicense(auth);
|
||||
}
|
||||
|
||||
@Put('me/license')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.UserLicenseUpdate })
|
||||
async setUserLicense(@Auth() auth: AuthDto, @Body() license: LicenseKeyDto): Promise<LicenseResponseDto> {
|
||||
return this.service.setLicense(auth, license);
|
||||
}
|
||||
|
||||
@Delete('me/license')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.UserLicenseDelete })
|
||||
async deleteUserLicense(@Auth() auth: AuthDto): Promise<void> {
|
||||
await this.service.deleteLicense(auth);
|
||||
}
|
||||
|
||||
@Get('me/onboarding')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.UserOnboardingRead })
|
||||
getUserOnboarding(@Auth() auth: AuthDto): Promise<OnboardingResponseDto> {
|
||||
return this.service.getOnboarding(auth);
|
||||
}
|
||||
|
||||
@Put('me/onboarding')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.UserOnboardingUpdate })
|
||||
async setUserOnboarding(@Auth() auth: AuthDto, @Body() Onboarding: OnboardingDto): Promise<OnboardingResponseDto> {
|
||||
return this.service.setOnboarding(auth, Onboarding);
|
||||
}
|
||||
|
||||
@Delete('me/onboarding')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.UserOnboardingDelete })
|
||||
async deleteUserOnboarding(@Auth() auth: AuthDto): Promise<void> {
|
||||
await this.service.deleteOnboarding(auth);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.UserRead })
|
||||
getUser(@Param() { id }: UUIDParamDto): Promise<UserResponseDto> {
|
||||
return this.service.get(id);
|
||||
}
|
||||
@@ -116,7 +116,7 @@ export class UserController {
|
||||
@ApiConsumes('multipart/form-data')
|
||||
@ApiBody({ description: 'A new avatar for the user', type: CreateProfileImageDto })
|
||||
@Post('profile-image')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.UserProfileImageUpdate })
|
||||
createProfileImage(
|
||||
@Auth() auth: AuthDto,
|
||||
@UploadedFile() fileInfo: Express.Multer.File,
|
||||
@@ -126,14 +126,14 @@ export class UserController {
|
||||
|
||||
@Delete('profile-image')
|
||||
@HttpCode(HttpStatus.NO_CONTENT)
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.UserProfileImageDelete })
|
||||
deleteProfileImage(@Auth() auth: AuthDto): Promise<void> {
|
||||
return this.service.deleteProfileImage(auth);
|
||||
}
|
||||
|
||||
@Get(':id/profile-image')
|
||||
@FileResponse()
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.UserProfileImageRead })
|
||||
async getProfileImage(@Res() res: Response, @Next() next: NextFunction, @Param() { id }: UUIDParamDto) {
|
||||
await sendFile(res, next, () => this.service.getProfileImage(id), this.logger);
|
||||
}
|
||||
|
||||
@@ -87,31 +87,45 @@ export enum Permission {
|
||||
AssetRead = 'asset.read',
|
||||
AssetUpdate = 'asset.update',
|
||||
AssetDelete = 'asset.delete',
|
||||
AssetStatistics = 'asset.statistics',
|
||||
AssetShare = 'asset.share',
|
||||
AssetView = 'asset.view',
|
||||
AssetDownload = 'asset.download',
|
||||
AssetUpload = 'asset.upload',
|
||||
AssetReplace = 'asset.replace',
|
||||
|
||||
AlbumCreate = 'album.create',
|
||||
AlbumRead = 'album.read',
|
||||
AlbumUpdate = 'album.update',
|
||||
AlbumDelete = 'album.delete',
|
||||
AlbumStatistics = 'album.statistics',
|
||||
|
||||
AlbumAddAsset = 'album.addAsset',
|
||||
AlbumRemoveAsset = 'album.removeAsset',
|
||||
AlbumShare = 'album.share',
|
||||
AlbumDownload = 'album.download',
|
||||
|
||||
AlbumAssetCreate = 'albumAsset.create',
|
||||
AlbumAssetDelete = 'albumAsset.delete',
|
||||
|
||||
AlbumUserCreate = 'albumUser.create',
|
||||
AlbumUserUpdate = 'albumUser.update',
|
||||
AlbumUserDelete = 'albumUser.delete',
|
||||
|
||||
AuthChangePassword = 'auth.changePassword',
|
||||
|
||||
AuthDeviceDelete = 'authDevice.delete',
|
||||
|
||||
ArchiveRead = 'archive.read',
|
||||
|
||||
DuplicateRead = 'duplicate.read',
|
||||
DuplicateDelete = 'duplicate.delete',
|
||||
|
||||
FaceCreate = 'face.create',
|
||||
FaceRead = 'face.read',
|
||||
FaceUpdate = 'face.update',
|
||||
FaceDelete = 'face.delete',
|
||||
|
||||
JobCreate = 'job.create',
|
||||
JobRead = 'job.read',
|
||||
|
||||
LibraryCreate = 'library.create',
|
||||
LibraryRead = 'library.read',
|
||||
LibraryUpdate = 'library.update',
|
||||
@@ -125,6 +139,10 @@ export enum Permission {
|
||||
MemoryRead = 'memory.read',
|
||||
MemoryUpdate = 'memory.update',
|
||||
MemoryDelete = 'memory.delete',
|
||||
MemoryStatistics = 'memory.statistics',
|
||||
|
||||
MemoryAssetCreate = 'memoryAsset.create',
|
||||
MemoryAssetDelete = 'memoryAsset.delete',
|
||||
|
||||
NotificationCreate = 'notification.create',
|
||||
NotificationRead = 'notification.read',
|
||||
@@ -144,6 +162,19 @@ export enum Permission {
|
||||
PersonMerge = 'person.merge',
|
||||
PersonReassign = 'person.reassign',
|
||||
|
||||
PinCodeCreate = 'pinCode.create',
|
||||
PinCodeUpdate = 'pinCode.update',
|
||||
PinCodeDelete = 'pinCode.delete',
|
||||
|
||||
ServerAbout = 'server.about',
|
||||
ServerApkLinks = 'server.apkLinks',
|
||||
ServerStorage = 'server.storage',
|
||||
ServerStatistics = 'server.statistics',
|
||||
|
||||
ServerLicenseRead = 'serverLicense.read',
|
||||
ServerLicenseUpdate = 'serverLicense.update',
|
||||
ServerLicenseDelete = 'serverLicense.delete',
|
||||
|
||||
SessionCreate = 'session.create',
|
||||
SessionRead = 'session.read',
|
||||
SessionUpdate = 'session.update',
|
||||
@@ -160,6 +191,11 @@ export enum Permission {
|
||||
StackUpdate = 'stack.update',
|
||||
StackDelete = 'stack.delete',
|
||||
|
||||
SyncStream = 'sync.stream',
|
||||
SyncCheckpointRead = 'syncCheckpoint.read',
|
||||
SyncCheckpointUpdate = 'syncCheckpoint.update',
|
||||
SyncCheckpointDelete = 'syncCheckpoint.delete',
|
||||
|
||||
SystemConfigRead = 'systemConfig.read',
|
||||
SystemConfigUpdate = 'systemConfig.update',
|
||||
|
||||
@@ -172,10 +208,30 @@ export enum Permission {
|
||||
TagDelete = 'tag.delete',
|
||||
TagAsset = 'tag.asset',
|
||||
|
||||
AdminUserCreate = 'admin.user.create',
|
||||
AdminUserRead = 'admin.user.read',
|
||||
AdminUserUpdate = 'admin.user.update',
|
||||
AdminUserDelete = 'admin.user.delete',
|
||||
UserRead = 'user.read',
|
||||
UserUpdate = 'user.update',
|
||||
|
||||
UserLicenseCreate = 'userLicense.create',
|
||||
UserLicenseRead = 'userLicense.read',
|
||||
UserLicenseUpdate = 'userLicense.update',
|
||||
UserLicenseDelete = 'userLicense.delete',
|
||||
|
||||
UserOnboardingRead = 'userOnboarding.read',
|
||||
UserOnboardingUpdate = 'userOnboarding.update',
|
||||
UserOnboardingDelete = 'userOnboarding.delete',
|
||||
|
||||
UserPreferenceRead = 'userPreference.read',
|
||||
UserPreferenceUpdate = 'userPreference.update',
|
||||
|
||||
UserProfileImageCreate = 'userProfileImage.create',
|
||||
UserProfileImageRead = 'userProfileImage.read',
|
||||
UserProfileImageUpdate = 'userProfileImage.update',
|
||||
UserProfileImageDelete = 'userProfileImage.delete',
|
||||
|
||||
AdminUserCreate = 'adminUser.create',
|
||||
AdminUserRead = 'adminUser.read',
|
||||
AdminUserUpdate = 'adminUser.update',
|
||||
AdminUserDelete = 'adminUser.delete',
|
||||
}
|
||||
|
||||
export enum SharedLinkType {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Kysely, sql } from 'kysely';
|
||||
|
||||
const items = [
|
||||
{ oldName: 'album.addAsset', newName: 'albumAsset.create' },
|
||||
{ oldName: 'album.removeAsset', newName: 'albumAsset.delete' },
|
||||
{ oldName: 'admin.user.create', newName: 'adminUser.create' },
|
||||
{ oldName: 'admin.user.read', newName: 'adminUser.read' },
|
||||
{ oldName: 'admin.user.update', newName: 'adminUser.update' },
|
||||
{ oldName: 'admin.user.delete', newName: 'adminUser.delete' },
|
||||
];
|
||||
|
||||
export async function up(db: Kysely<any>): Promise<void> {
|
||||
for (const { oldName, newName } of items) {
|
||||
await sql`UPDATE "api_key" SET "permissions" = array_replace("permissions", ${oldName}, ${newName})`.execute(db);
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(db: Kysely<any>): Promise<void> {
|
||||
for (const { oldName, newName } of items) {
|
||||
await sql`UPDATE "api_key" SET "permissions" = array_replace("permissions", ${newName}, ${oldName})`.execute(db);
|
||||
}
|
||||
}
|
||||
@@ -158,7 +158,7 @@ export class AlbumService extends BaseService {
|
||||
|
||||
async addAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise<BulkIdResponseDto[]> {
|
||||
const album = await this.findOrFail(id, { withAssets: false });
|
||||
await this.requireAccess({ auth, permission: Permission.AlbumAddAsset, ids: [id] });
|
||||
await this.requireAccess({ auth, permission: Permission.AlbumAssetCreate, ids: [id] });
|
||||
|
||||
const results = await addAssets(
|
||||
auth,
|
||||
@@ -187,7 +187,7 @@ export class AlbumService extends BaseService {
|
||||
}
|
||||
|
||||
async removeAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise<BulkIdResponseDto[]> {
|
||||
await this.requireAccess({ auth, permission: Permission.AlbumRemoveAsset, ids: [id] });
|
||||
await this.requireAccess({ auth, permission: Permission.AlbumAssetDelete, ids: [id] });
|
||||
|
||||
const album = await this.findOrFail(id, { withAssets: false });
|
||||
const results = await removeAssets(
|
||||
|
||||
@@ -92,7 +92,7 @@ const checkSharedLinkAccess = async (
|
||||
return sharedLink.allowDownload ? await access.album.checkSharedLinkAccess(sharedLinkId, ids) : new Set();
|
||||
}
|
||||
|
||||
case Permission.AlbumAddAsset: {
|
||||
case Permission.AlbumAssetCreate: {
|
||||
return sharedLink.allowUpload ? await access.album.checkSharedLinkAccess(sharedLinkId, ids) : new Set();
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ const checkOtherAccess = async (access: AccessRepository, request: OtherAccessRe
|
||||
return setUnion(isOwner, isShared);
|
||||
}
|
||||
|
||||
case Permission.AlbumAddAsset: {
|
||||
case Permission.AlbumAssetCreate: {
|
||||
const isOwner = await access.album.checkOwnerAccess(auth.user.id, ids);
|
||||
const isShared = await access.album.checkSharedAlbumAccess(
|
||||
auth.user.id,
|
||||
@@ -195,7 +195,7 @@ const checkOtherAccess = async (access: AccessRepository, request: OtherAccessRe
|
||||
return setUnion(isOwner, isShared);
|
||||
}
|
||||
|
||||
case Permission.AlbumRemoveAsset: {
|
||||
case Permission.AlbumAssetDelete: {
|
||||
const isOwner = await access.album.checkOwnerAccess(auth.user.id, ids);
|
||||
const isShared = await access.album.checkSharedAlbumAccess(
|
||||
auth.user.id,
|
||||
|
||||
Reference in New Issue
Block a user