immich/server/apps/immich/src/api-v1/asset/asset.controller.ts
Alex f980a2f27a
Add asset repository and refactor asset service (#540)
* build endpoint to get asset count by month

* Added asset repository

* Added create asset

* get asset by device ID

* Added test for existing methods

* Refactor additional endpoint

* Refactor database api to get curated locations and curated objects

* Refactor get search properties

* Fixed cookies parsing for websocket

* Added API to get asset count by time group

* Remove unused code
2022-08-26 22:53:37 -07:00

216 lines
7.6 KiB
TypeScript

import {
Controller,
Post,
UseInterceptors,
Body,
UseGuards,
Get,
Param,
ValidationPipe,
Query,
Response,
Headers,
Delete,
Logger,
HttpCode,
BadRequestException,
UploadedFile,
} from '@nestjs/common';
import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard';
import { AssetService } from './asset.service';
import { FileInterceptor } from '@nestjs/platform-express';
import { assetUploadOption } from '../../config/asset-upload.config';
import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
import { ServeFileDto } from './dto/serve-file.dto';
import { Response as Res } from 'express';
import { BackgroundTaskService } from '../../modules/background-task/background-task.service';
import { DeleteAssetDto } from './dto/delete-asset.dto';
import { SearchAssetDto } from './dto/search-asset.dto';
import { CommunicationGateway } from '../communication/communication.gateway';
import { InjectQueue } from '@nestjs/bull';
import { Queue } from 'bull';
import { IAssetUploadedJob } from '@app/job/index';
import { assetUploadedQueueName } from '@app/job/constants/queue-name.constant';
import { assetUploadedProcessorName } from '@app/job/constants/job-name.constant';
import { CheckDuplicateAssetDto } from './dto/check-duplicate-asset.dto';
import { ApiBearerAuth, ApiBody, ApiConsumes, ApiResponse, ApiTags } from '@nestjs/swagger';
import { CuratedObjectsResponseDto } from './response-dto/curated-objects-response.dto';
import { CuratedLocationsResponseDto } from './response-dto/curated-locations-response.dto';
import { AssetResponseDto } from './response-dto/asset-response.dto';
import { CheckDuplicateAssetResponseDto } from './response-dto/check-duplicate-asset-response.dto';
import { AssetFileUploadDto } from './dto/asset-file-upload.dto';
import { CreateAssetDto } from './dto/create-asset.dto';
import { AssetFileUploadResponseDto } from './response-dto/asset-file-upload-response.dto';
import { DeleteAssetResponseDto, DeleteAssetStatusEnum } from './response-dto/delete-asset-response.dto';
import { GetAssetThumbnailDto } from './dto/get-asset-thumbnail.dto';
import { AssetCountByTimeGroupResponseDto } from './response-dto/asset-count-by-time-group-response.dto';
import { GetAssetCountByTimeGroupDto } from './dto/get-asset-count-by-time-group.dto';
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@ApiTags('Asset')
@Controller('asset')
export class AssetController {
constructor(
private wsCommunicateionGateway: CommunicationGateway,
private assetService: AssetService,
private backgroundTaskService: BackgroundTaskService,
@InjectQueue(assetUploadedQueueName)
private assetUploadedQueue: Queue<IAssetUploadedJob>,
) {}
@Post('upload')
@UseInterceptors(FileInterceptor('assetData', assetUploadOption))
@ApiConsumes('multipart/form-data')
@ApiBody({
description: 'Asset Upload Information',
type: AssetFileUploadDto,
})
async uploadFile(
@GetAuthUser() authUser: AuthUserDto,
@UploadedFile() file: Express.Multer.File,
@Body(ValidationPipe) assetInfo: CreateAssetDto,
): Promise<AssetFileUploadResponseDto> {
try {
const savedAsset = await this.assetService.createUserAsset(authUser, assetInfo, file.path, file.mimetype);
if (!savedAsset) {
throw new BadRequestException('Asset not created');
}
await this.assetUploadedQueue.add(
assetUploadedProcessorName,
{ asset: savedAsset, fileName: file.originalname, fileSize: file.size },
{ jobId: savedAsset.id },
);
return new AssetFileUploadResponseDto(savedAsset.id);
} catch (e) {
Logger.error(`Error uploading file ${e}`);
throw new BadRequestException(`Error uploading file`, `${e}`);
}
}
@Get('/download')
async downloadFile(
@GetAuthUser() authUser: AuthUserDto,
@Response({ passthrough: true }) res: Res,
@Query(new ValidationPipe({ transform: true })) query: ServeFileDto,
): Promise<any> {
return this.assetService.downloadFile(query, res);
}
@Get('/file')
async serveFile(
@Headers() headers: Record<string, string>,
@GetAuthUser() authUser: AuthUserDto,
@Response({ passthrough: true }) res: Res,
@Query(new ValidationPipe({ transform: true })) query: ServeFileDto,
): Promise<any> {
return this.assetService.serveFile(authUser, query, res, headers);
}
@Get('/thumbnail/:assetId')
async getAssetThumbnail(
@Param('assetId') assetId: string,
@Query(new ValidationPipe({ transform: true })) query: GetAssetThumbnailDto,
): Promise<any> {
return this.assetService.getAssetThumbnail(assetId, query);
}
@Get('/curated-objects')
async getCuratedObjects(@GetAuthUser() authUser: AuthUserDto): Promise<CuratedObjectsResponseDto[]> {
return this.assetService.getCuratedObject(authUser);
}
@Get('/curated-locations')
async getCuratedLocations(@GetAuthUser() authUser: AuthUserDto): Promise<CuratedLocationsResponseDto[]> {
return this.assetService.getCuratedLocation(authUser);
}
@Get('/search-terms')
async getAssetSearchTerms(@GetAuthUser() authUser: AuthUserDto): Promise<string[]> {
return this.assetService.getAssetSearchTerm(authUser);
}
@Post('/search')
async searchAsset(
@GetAuthUser() authUser: AuthUserDto,
@Body(ValidationPipe) searchAssetDto: SearchAssetDto,
): Promise<AssetResponseDto[]> {
return this.assetService.searchAsset(authUser, searchAssetDto);
}
@Get('/count-by-date')
async getAssetCountByTimeGroup(
@GetAuthUser() authUser: AuthUserDto,
@Body(ValidationPipe) getAssetCountByTimeGroupDto: GetAssetCountByTimeGroupDto,
): Promise<AssetCountByTimeGroupResponseDto> {
return this.assetService.getAssetCountByTimeGroup(authUser, getAssetCountByTimeGroupDto);
}
/**
* Get all AssetEntity belong to the user
*/
@Get('/')
async getAllAssets(@GetAuthUser() authUser: AuthUserDto): Promise<AssetResponseDto[]> {
return await this.assetService.getAllAssets(authUser);
}
/**
* Get all asset of a device that are in the database, ID only.
*/
@Get('/:deviceId')
async getUserAssetsByDeviceId(@GetAuthUser() authUser: AuthUserDto, @Param('deviceId') deviceId: string) {
return await this.assetService.getUserAssetsByDeviceId(authUser, deviceId);
}
/**
* Get a single asset's information
*/
@Get('/assetById/:assetId')
async getAssetById(
@GetAuthUser() authUser: AuthUserDto,
@Param('assetId') assetId: string,
): Promise<AssetResponseDto> {
return await this.assetService.getAssetById(authUser, assetId);
}
@Delete('/')
async deleteAsset(
@GetAuthUser() authUser: AuthUserDto,
@Body(ValidationPipe) assetIds: DeleteAssetDto,
): Promise<DeleteAssetResponseDto[]> {
const deleteAssetList: AssetResponseDto[] = [];
for (const id of assetIds.ids) {
const assets = await this.assetService.getAssetById(authUser, id);
if (!assets) {
continue;
}
deleteAssetList.push(assets);
}
const result = await this.assetService.deleteAssetById(authUser, assetIds);
result.forEach((res) => {
deleteAssetList.filter((a) => a.id == res.id && res.status == DeleteAssetStatusEnum.SUCCESS);
});
await this.backgroundTaskService.deleteFileOnDisk(deleteAssetList);
return result;
}
/**
* Check duplicated asset before uploading - for Web upload used
*/
@Post('/check')
@HttpCode(200)
async checkDuplicateAsset(
@GetAuthUser() authUser: AuthUserDto,
@Body(ValidationPipe) checkDuplicateAssetDto: CheckDuplicateAssetDto,
): Promise<CheckDuplicateAssetResponseDto> {
return await this.assetService.checkDuplicatedAsset(authUser, checkDuplicateAssetDto);
}
}