mirror of
https://github.com/immich-app/immich
synced 2025-11-07 17:27:20 +00:00
WIP refactor container and queuing system (#206)
* refactor microservices to machine-learning * Update tGithub issue template with correct task syntax * Added microservices container * Communicate between service based on queue system * added dependency * Fixed problem with having to import BullQueue into the individual service * Added todo * refactor server into monorepo with microservices * refactor database and entity to library * added simple migration * Move migrations and database config to library * Migration works in library * Cosmetic change in logging message * added user dto * Fixed issue with testing not able to find the shared library * Clean up library mapping path * Added webp generator to microservices * Update Github Action build latest * Fixed issue NPM cannot install due to conflict witl Bull Queue * format project with prettier * Modified docker-compose file * Add GH Action for Staging build: * Fixed GH action job name * Modified GH Action to only build & push latest when pushing to main * Added Test 2e2 Github Action * Added Test 2e2 Github Action * Implemented microservice to extract exif * Added cronjob to scan and generate webp thumbnail at midnight * Refactor to ireduce hit time to database when running microservices * Added error handling to asset services that handle read file from disk * Added video transcoding queue to process one video at a time * Fixed loading spinner on web while loading covering the info panel * Add mechanism to show new release announcement to web and mobile app (#209) * Added changelog page * Fixed issues based on PR comments * Fixed issue with video transcoding run on the server * Change entry point content for backward combatibility when starting up server * Added announcement box * Added error handling to failed silently when the app version checking is not able to make the request to GITHUB * Added new version announcement overlay * Update message * Added messages * Added logic to check and show announcement * Add method to handle saving new version * Added button to dimiss the acknowledge message * Up version for deployment to the app store
This commit is contained in:
parent
397f8c70b4
commit
a8220172f8
192 changed files with 1823 additions and 2117 deletions
174
server/apps/immich/src/api-v1/asset/asset.controller.ts
Normal file
174
server/apps/immich/src/api-v1/asset/asset.controller.ts
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
import {
|
||||
Controller,
|
||||
Post,
|
||||
UseInterceptors,
|
||||
UploadedFiles,
|
||||
Body,
|
||||
UseGuards,
|
||||
Get,
|
||||
Param,
|
||||
ValidationPipe,
|
||||
StreamableFile,
|
||||
Query,
|
||||
Response,
|
||||
Headers,
|
||||
Delete,
|
||||
Logger,
|
||||
Patch,
|
||||
} from '@nestjs/common';
|
||||
import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard';
|
||||
import { AssetService } from './asset.service';
|
||||
import { FileFieldsInterceptor } from '@nestjs/platform-express';
|
||||
import { assetUploadOption } from '../../config/asset-upload.config';
|
||||
import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
|
||||
import { CreateAssetDto } from './dto/create-asset.dto';
|
||||
import { ServeFileDto } from './dto/serve-file.dto';
|
||||
import { AssetEntity } from '@app/database/entities/asset.entity';
|
||||
import { GetAllAssetQueryDto } from './dto/get-all-asset-query.dto';
|
||||
import { Response as Res } from 'express';
|
||||
import { GetNewAssetQueryDto } from './dto/get-new-asset-query.dto';
|
||||
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';
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Controller('asset')
|
||||
export class AssetController {
|
||||
constructor(
|
||||
private wsCommunicateionGateway: CommunicationGateway,
|
||||
private assetService: AssetService,
|
||||
private backgroundTaskService: BackgroundTaskService,
|
||||
|
||||
@InjectQueue('asset-uploaded-queue')
|
||||
private assetUploadedQueue: Queue,
|
||||
) {}
|
||||
|
||||
@Post('upload')
|
||||
@UseInterceptors(
|
||||
FileFieldsInterceptor(
|
||||
[
|
||||
{ name: 'assetData', maxCount: 1 },
|
||||
{ name: 'thumbnailData', maxCount: 1 },
|
||||
],
|
||||
assetUploadOption,
|
||||
),
|
||||
)
|
||||
async uploadFile(
|
||||
@GetAuthUser() authUser,
|
||||
@UploadedFiles() uploadFiles: { assetData: Express.Multer.File[]; thumbnailData?: Express.Multer.File[] },
|
||||
@Body(ValidationPipe) assetInfo: CreateAssetDto,
|
||||
) {
|
||||
for (const file of uploadFiles.assetData) {
|
||||
try {
|
||||
const savedAsset = await this.assetService.createUserAsset(authUser, assetInfo, file.path, file.mimetype);
|
||||
|
||||
if (uploadFiles.thumbnailData != null && savedAsset) {
|
||||
const assetWithThumbnail = await this.assetService.updateThumbnailInfo(
|
||||
savedAsset,
|
||||
uploadFiles.thumbnailData[0].path,
|
||||
);
|
||||
|
||||
await this.assetUploadedQueue.add(
|
||||
'asset-uploaded',
|
||||
{ asset: assetWithThumbnail, fileName: file.originalname, fileSize: file.size, hasThumbnail: true },
|
||||
{ jobId: savedAsset.id },
|
||||
);
|
||||
} else {
|
||||
await this.assetUploadedQueue.add(
|
||||
'asset-uploaded',
|
||||
{ asset: savedAsset, fileName: file.originalname, fileSize: file.size, hasThumbnail: false },
|
||||
{ jobId: savedAsset.id },
|
||||
);
|
||||
}
|
||||
|
||||
this.wsCommunicateionGateway.server.to(savedAsset.userId).emit('on_upload_success', JSON.stringify(savedAsset));
|
||||
} catch (e) {
|
||||
Logger.error(`Error receiving upload file ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
return 'ok';
|
||||
}
|
||||
|
||||
@Get('/download')
|
||||
async downloadFile(
|
||||
@GetAuthUser() authUser: AuthUserDto,
|
||||
@Response({ passthrough: true }) res: Res,
|
||||
@Query(ValidationPipe) query: ServeFileDto,
|
||||
) {
|
||||
return this.assetService.downloadFile(query, res);
|
||||
}
|
||||
|
||||
@Get('/file')
|
||||
async serveFile(
|
||||
@Headers() headers,
|
||||
@GetAuthUser() authUser: AuthUserDto,
|
||||
@Response({ passthrough: true }) res: Res,
|
||||
@Query(ValidationPipe) query: ServeFileDto,
|
||||
): Promise<StreamableFile> {
|
||||
return this.assetService.serveFile(authUser, query, res, headers);
|
||||
}
|
||||
|
||||
@Get('/thumbnail/:assetId')
|
||||
async getAssetThumbnail(@Param('assetId') assetId: string): Promise<StreamableFile> {
|
||||
return await this.assetService.getAssetThumbnail(assetId);
|
||||
}
|
||||
|
||||
@Get('/allObjects')
|
||||
async getCuratedObject(@GetAuthUser() authUser: AuthUserDto) {
|
||||
return this.assetService.getCuratedObject(authUser);
|
||||
}
|
||||
|
||||
@Get('/allLocation')
|
||||
async getCuratedLocation(@GetAuthUser() authUser: AuthUserDto) {
|
||||
return this.assetService.getCuratedLocation(authUser);
|
||||
}
|
||||
|
||||
@Get('/searchTerm')
|
||||
async getAssetSearchTerm(@GetAuthUser() authUser: AuthUserDto) {
|
||||
return this.assetService.getAssetSearchTerm(authUser);
|
||||
}
|
||||
|
||||
@Post('/search')
|
||||
async searchAsset(@GetAuthUser() authUser: AuthUserDto, @Body(ValidationPipe) searchAssetDto: SearchAssetDto) {
|
||||
return this.assetService.searchAsset(authUser, searchAssetDto);
|
||||
}
|
||||
|
||||
@Get('/')
|
||||
async getAllAssets(@GetAuthUser() authUser: AuthUserDto) {
|
||||
return await this.assetService.getAllAssets(authUser);
|
||||
}
|
||||
|
||||
@Get('/:deviceId')
|
||||
async getUserAssetsByDeviceId(@GetAuthUser() authUser: AuthUserDto, @Param('deviceId') deviceId: string) {
|
||||
return await this.assetService.getUserAssetsByDeviceId(authUser, deviceId);
|
||||
}
|
||||
|
||||
@Get('/assetById/:assetId')
|
||||
async getAssetById(@GetAuthUser() authUser: AuthUserDto, @Param('assetId') assetId) {
|
||||
return await this.assetService.getAssetById(authUser, assetId);
|
||||
}
|
||||
|
||||
@Delete('/')
|
||||
async deleteAssetById(@GetAuthUser() authUser: AuthUserDto, @Body(ValidationPipe) assetIds: DeleteAssetDto) {
|
||||
const deleteAssetList: AssetEntity[] = [];
|
||||
|
||||
for (const id of assetIds.ids) {
|
||||
const assets = await this.assetService.getAssetById(authUser, id);
|
||||
deleteAssetList.push(assets);
|
||||
}
|
||||
|
||||
const result = await this.assetService.deleteAssetById(authUser, assetIds);
|
||||
|
||||
result.forEach((res) => {
|
||||
deleteAssetList.filter((a) => a.id == res.id && res.status == 'success');
|
||||
});
|
||||
|
||||
await this.backgroundTaskService.deleteFileOnDisk(deleteAssetList);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue