mirror of
				https://github.com/KevinMidboe/immich.git
				synced 2025-10-29 17:40:28 +00:00 
			
		
		
		
	Add OpenAPI Specs and Response DTOs (#320)
* Added swagger bearer auth method authentication accordingly * Update Auth endpoint * Added additional api information for authentication * Added Swagger CLI pluggin * Added DTO for /user endpoint * Added /device-info reponse DTOs * Implement server version * Added DTOs for /server-info * Added DTOs for /assets * Added album to Swagger group * Added generated specs file * Add Client API generator for web * Remove incorrectly placed node_modules * Created class to handle access token * Remove password and hash when getting all user * PR feedback * Fixed video from CLI doesn't get metadata extracted * Fixed issue with TSConfig to work with generated openAPI * PR feedback * Remove console.log
This commit is contained in:
		@@ -22,20 +22,23 @@ import { AddUsersDto } from './dto/add-users.dto';
 | 
				
			|||||||
import { RemoveAssetsDto } from './dto/remove-assets.dto';
 | 
					import { RemoveAssetsDto } from './dto/remove-assets.dto';
 | 
				
			||||||
import { UpdateAlbumDto } from './dto/update-album.dto';
 | 
					import { UpdateAlbumDto } from './dto/update-album.dto';
 | 
				
			||||||
import { GetAlbumsDto } from './dto/get-albums.dto';
 | 
					import { GetAlbumsDto } from './dto/get-albums.dto';
 | 
				
			||||||
 | 
					import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO might be worth creating a AlbumParamsDto that validates `albumId` instead of using the pipe.
 | 
					// TODO might be worth creating a AlbumParamsDto that validates `albumId` instead of using the pipe.
 | 
				
			||||||
@UseGuards(JwtAuthGuard)
 | 
					@UseGuards(JwtAuthGuard)
 | 
				
			||||||
 | 
					@ApiBearerAuth()
 | 
				
			||||||
 | 
					@ApiTags('Album')
 | 
				
			||||||
@Controller('album')
 | 
					@Controller('album')
 | 
				
			||||||
export class AlbumController {
 | 
					export class AlbumController {
 | 
				
			||||||
  constructor(private readonly albumService: AlbumService) {}
 | 
					  constructor(private readonly albumService: AlbumService) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Post()
 | 
					  @Post()
 | 
				
			||||||
  async create(@GetAuthUser() authUser: AuthUserDto, @Body(ValidationPipe) createAlbumDto: CreateAlbumDto) {
 | 
					  async createAlbum(@GetAuthUser() authUser: AuthUserDto, @Body(ValidationPipe) createAlbumDto: CreateAlbumDto) {
 | 
				
			||||||
    return this.albumService.create(authUser, createAlbumDto);
 | 
					    return this.albumService.create(authUser, createAlbumDto);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Put('/:albumId/users')
 | 
					  @Put('/:albumId/users')
 | 
				
			||||||
  async addUsers(
 | 
					  async addUsersToAlbum(
 | 
				
			||||||
    @GetAuthUser() authUser: AuthUserDto,
 | 
					    @GetAuthUser() authUser: AuthUserDto,
 | 
				
			||||||
    @Body(ValidationPipe) addUsersDto: AddUsersDto,
 | 
					    @Body(ValidationPipe) addUsersDto: AddUsersDto,
 | 
				
			||||||
    @Param('albumId', new ParseUUIDPipe({ version: '4' })) albumId: string,
 | 
					    @Param('albumId', new ParseUUIDPipe({ version: '4' })) albumId: string,
 | 
				
			||||||
@@ -44,7 +47,7 @@ export class AlbumController {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Put('/:albumId/assets')
 | 
					  @Put('/:albumId/assets')
 | 
				
			||||||
  async addAssets(
 | 
					  async addAssetsToAlbum(
 | 
				
			||||||
    @GetAuthUser() authUser: AuthUserDto,
 | 
					    @GetAuthUser() authUser: AuthUserDto,
 | 
				
			||||||
    @Body(ValidationPipe) addAssetsDto: AddAssetsDto,
 | 
					    @Body(ValidationPipe) addAssetsDto: AddAssetsDto,
 | 
				
			||||||
    @Param('albumId', new ParseUUIDPipe({ version: '4' })) albumId: string,
 | 
					    @Param('albumId', new ParseUUIDPipe({ version: '4' })) albumId: string,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,15 +2,15 @@ import { AlbumEntity } from '../../../../../../libs/database/src/entities/album.
 | 
				
			|||||||
import { UserResponseDto, mapUser } from '../../user/response-dto/user-response.dto';
 | 
					import { UserResponseDto, mapUser } from '../../user/response-dto/user-response.dto';
 | 
				
			||||||
import { AssetResponseDto, mapAsset } from '../../asset/response-dto/asset-response.dto';
 | 
					import { AssetResponseDto, mapAsset } from '../../asset/response-dto/asset-response.dto';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface AlbumResponseDto {
 | 
					export class AlbumResponseDto {
 | 
				
			||||||
  id: string;
 | 
					  id!: string;
 | 
				
			||||||
  ownerId: string;
 | 
					  ownerId!: string;
 | 
				
			||||||
  albumName: string;
 | 
					  albumName!: string;
 | 
				
			||||||
  createdAt: string;
 | 
					  createdAt!: string;
 | 
				
			||||||
  albumThumbnailAssetId: string | null;
 | 
					  albumThumbnailAssetId!: string | null;
 | 
				
			||||||
  shared: boolean;
 | 
					  shared!: boolean;
 | 
				
			||||||
  sharedUsers: UserResponseDto[];
 | 
					  sharedUsers!: UserResponseDto[];
 | 
				
			||||||
  assets: AssetResponseDto[];
 | 
					  assets!: AssetResponseDto[];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function mapAlbum(entity: AlbumEntity): AlbumResponseDto {
 | 
					export function mapAlbum(entity: AlbumEntity): AlbumResponseDto {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,8 +36,14 @@ import { IAssetUploadedJob } from '@app/job/index';
 | 
				
			|||||||
import { assetUploadedQueueName } from '@app/job/constants/queue-name.constant';
 | 
					import { assetUploadedQueueName } from '@app/job/constants/queue-name.constant';
 | 
				
			||||||
import { assetUploadedProcessorName } from '@app/job/constants/job-name.constant';
 | 
					import { assetUploadedProcessorName } from '@app/job/constants/job-name.constant';
 | 
				
			||||||
import { CheckDuplicateAssetDto } from './dto/check-duplicate-asset.dto';
 | 
					import { CheckDuplicateAssetDto } from './dto/check-duplicate-asset.dto';
 | 
				
			||||||
 | 
					import { ApiBearerAuth, 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';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@UseGuards(JwtAuthGuard)
 | 
					@UseGuards(JwtAuthGuard)
 | 
				
			||||||
 | 
					@ApiBearerAuth()
 | 
				
			||||||
 | 
					@ApiTags('Asset')
 | 
				
			||||||
@Controller('asset')
 | 
					@Controller('asset')
 | 
				
			||||||
export class AssetController {
 | 
					export class AssetController {
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
@@ -89,7 +95,7 @@ export class AssetController {
 | 
				
			|||||||
    @GetAuthUser() authUser: AuthUserDto,
 | 
					    @GetAuthUser() authUser: AuthUserDto,
 | 
				
			||||||
    @Response({ passthrough: true }) res: Res,
 | 
					    @Response({ passthrough: true }) res: Res,
 | 
				
			||||||
    @Query(ValidationPipe) query: ServeFileDto,
 | 
					    @Query(ValidationPipe) query: ServeFileDto,
 | 
				
			||||||
  ) {
 | 
					  ): Promise<StreamableFile> {
 | 
				
			||||||
    return this.assetService.downloadFile(query, res);
 | 
					    return this.assetService.downloadFile(query, res);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -109,43 +115,58 @@ export class AssetController {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Get('/allObjects')
 | 
					  @Get('/allObjects')
 | 
				
			||||||
  async getCuratedObject(@GetAuthUser() authUser: AuthUserDto) {
 | 
					  async getCuratedObjects(@GetAuthUser() authUser: AuthUserDto): Promise<CuratedObjectsResponseDto[]> {
 | 
				
			||||||
    return this.assetService.getCuratedObject(authUser);
 | 
					    return this.assetService.getCuratedObject(authUser);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Get('/allLocation')
 | 
					  @Get('/allLocation')
 | 
				
			||||||
  async getCuratedLocation(@GetAuthUser() authUser: AuthUserDto) {
 | 
					  async getCuratedLocations(@GetAuthUser() authUser: AuthUserDto): Promise<CuratedLocationsResponseDto[]> {
 | 
				
			||||||
    return this.assetService.getCuratedLocation(authUser);
 | 
					    return this.assetService.getCuratedLocation(authUser);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Get('/searchTerm')
 | 
					  @Get('/searchTerm')
 | 
				
			||||||
  async getAssetSearchTerm(@GetAuthUser() authUser: AuthUserDto) {
 | 
					  async getAssetSearchTerms(@GetAuthUser() authUser: AuthUserDto): Promise<String[]> {
 | 
				
			||||||
    return this.assetService.getAssetSearchTerm(authUser);
 | 
					    return this.assetService.getAssetSearchTerm(authUser);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Post('/search')
 | 
					  @Post('/search')
 | 
				
			||||||
  async searchAsset(@GetAuthUser() authUser: AuthUserDto, @Body(ValidationPipe) searchAssetDto: SearchAssetDto) {
 | 
					  async searchAsset(
 | 
				
			||||||
 | 
					    @GetAuthUser() authUser: AuthUserDto,
 | 
				
			||||||
 | 
					    @Body(ValidationPipe) searchAssetDto: SearchAssetDto,
 | 
				
			||||||
 | 
					  ): Promise<AssetResponseDto[]> {
 | 
				
			||||||
    return this.assetService.searchAsset(authUser, searchAssetDto);
 | 
					    return this.assetService.searchAsset(authUser, searchAssetDto);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Get all AssetEntity belong to the user
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
  @Get('/')
 | 
					  @Get('/')
 | 
				
			||||||
  async getAllAssets(@GetAuthUser() authUser: AuthUserDto) {
 | 
					  async getAllAssets(@GetAuthUser() authUser: AuthUserDto): Promise<AssetResponseDto[]> {
 | 
				
			||||||
    return await this.assetService.getAllAssets(authUser);
 | 
					    return await this.assetService.getAllAssets(authUser);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Get all asset of a device that are in the database, ID only.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
  @Get('/:deviceId')
 | 
					  @Get('/:deviceId')
 | 
				
			||||||
  async getUserAssetsByDeviceId(@GetAuthUser() authUser: AuthUserDto, @Param('deviceId') deviceId: string) {
 | 
					  async getUserAssetsByDeviceId(@GetAuthUser() authUser: AuthUserDto, @Param('deviceId') deviceId: string) {
 | 
				
			||||||
    return await this.assetService.getUserAssetsByDeviceId(authUser, deviceId);
 | 
					    return await this.assetService.getUserAssetsByDeviceId(authUser, deviceId);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Get a single asset's information
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
  @Get('/assetById/:assetId')
 | 
					  @Get('/assetById/:assetId')
 | 
				
			||||||
  async getAssetById(@GetAuthUser() authUser: AuthUserDto, @Param('assetId') assetId: string) {
 | 
					  async getAssetById(
 | 
				
			||||||
 | 
					    @GetAuthUser() authUser: AuthUserDto,
 | 
				
			||||||
 | 
					    @Param('assetId') assetId: string,
 | 
				
			||||||
 | 
					  ): Promise<AssetResponseDto> {
 | 
				
			||||||
    return await this.assetService.getAssetById(authUser, assetId);
 | 
					    return await this.assetService.getAssetById(authUser, assetId);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Delete('/')
 | 
					  @Delete('/')
 | 
				
			||||||
  async deleteAssetById(@GetAuthUser() authUser: AuthUserDto, @Body(ValidationPipe) assetIds: DeleteAssetDto) {
 | 
					  async deleteAsset(@GetAuthUser() authUser: AuthUserDto, @Body(ValidationPipe) assetIds: DeleteAssetDto) {
 | 
				
			||||||
    const deleteAssetList: AssetEntity[] = [];
 | 
					    const deleteAssetList: AssetResponseDto[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (const id of assetIds.ids) {
 | 
					    for (const id of assetIds.ids) {
 | 
				
			||||||
      const assets = await this.assetService.getAssetById(authUser, id);
 | 
					      const assets = await this.assetService.getAssetById(authUser, id);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,6 +19,8 @@ import { DeleteAssetDto } from './dto/delete-asset.dto';
 | 
				
			|||||||
import { SearchAssetDto } from './dto/search-asset.dto';
 | 
					import { SearchAssetDto } from './dto/search-asset.dto';
 | 
				
			||||||
import fs from 'fs/promises';
 | 
					import fs from 'fs/promises';
 | 
				
			||||||
import { CheckDuplicateAssetDto } from './dto/check-duplicate-asset.dto';
 | 
					import { CheckDuplicateAssetDto } from './dto/check-duplicate-asset.dto';
 | 
				
			||||||
 | 
					import { CuratedObjectsResponseDto } from './response-dto/curated-objects-response.dto';
 | 
				
			||||||
 | 
					import { AssetResponseDto, mapAsset } from './response-dto/asset-response.dto';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const fileInfo = promisify(stat);
 | 
					const fileInfo = promisify(stat);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -80,49 +82,55 @@ export class AssetService {
 | 
				
			|||||||
    return res;
 | 
					    return res;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public async getAllAssets(authUser: AuthUserDto) {
 | 
					  public async getAllAssets(authUser: AuthUserDto): Promise<AssetResponseDto[]> {
 | 
				
			||||||
    try {
 | 
					    const assets = await this.assetRepository.find({
 | 
				
			||||||
      return await this.assetRepository.find({
 | 
					      where: {
 | 
				
			||||||
        where: {
 | 
					        userId: authUser.id,
 | 
				
			||||||
          userId: authUser.id,
 | 
					        resizePath: Not(IsNull()),
 | 
				
			||||||
          resizePath: Not(IsNull()),
 | 
					      },
 | 
				
			||||||
        },
 | 
					      relations: ['exifInfo'],
 | 
				
			||||||
        relations: ['exifInfo'],
 | 
					      order: {
 | 
				
			||||||
        order: {
 | 
					        createdAt: 'DESC',
 | 
				
			||||||
          createdAt: 'DESC',
 | 
					      },
 | 
				
			||||||
        },
 | 
					    });
 | 
				
			||||||
      });
 | 
					
 | 
				
			||||||
    } catch (e) {
 | 
					    return assets.map((asset) => mapAsset(asset));
 | 
				
			||||||
      Logger.error(e, 'getAllAssets');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public async findOne(deviceId: string, assetId: string): Promise<AssetEntity> {
 | 
					  public async findAssetOfDevice(deviceId: string, assetId: string): Promise<AssetResponseDto> {
 | 
				
			||||||
    const rows = await this.assetRepository.query(
 | 
					    const rows = await this.assetRepository.query(
 | 
				
			||||||
      'SELECT * FROM assets a WHERE a."deviceAssetId" = $1 AND a."deviceId" = $2',
 | 
					      'SELECT * FROM assets a WHERE a."deviceAssetId" = $1 AND a."deviceId" = $2',
 | 
				
			||||||
      [assetId, deviceId],
 | 
					      [assetId, deviceId],
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (rows.lengh == 0) {
 | 
					    if (rows.lengh == 0) {
 | 
				
			||||||
      throw new BadRequestException('Not Found');
 | 
					      throw new NotFoundException('Not Found');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return rows[0] as AssetEntity;
 | 
					    const assetOnDevice = rows[0] as AssetEntity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return mapAsset(assetOnDevice);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public async getAssetById(authUser: AuthUserDto, assetId: string) {
 | 
					  public async getAssetById(authUser: AuthUserDto, assetId: string): Promise<AssetResponseDto> {
 | 
				
			||||||
    return await this.assetRepository.findOne({
 | 
					    const asset = await this.assetRepository.findOne({
 | 
				
			||||||
      where: {
 | 
					      where: {
 | 
				
			||||||
        id: assetId,
 | 
					        id: assetId,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      relations: ['exifInfo'],
 | 
					      relations: ['exifInfo'],
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!asset) {
 | 
				
			||||||
 | 
					      throw new NotFoundException('Asset not found');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return mapAsset(asset);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public async downloadFile(query: ServeFileDto, res: Res) {
 | 
					  public async downloadFile(query: ServeFileDto, res: Res) {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      let fileReadStream = null;
 | 
					      let fileReadStream = null;
 | 
				
			||||||
      const asset = await this.findOne(query.did, query.aid);
 | 
					      const asset = await this.findAssetOfDevice(query.did, query.aid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (query.isThumb === 'false' || !query.isThumb) {
 | 
					      if (query.isThumb === 'false' || !query.isThumb) {
 | 
				
			||||||
        const { size } = await fileInfo(asset.originalPath);
 | 
					        const { size } = await fileInfo(asset.originalPath);
 | 
				
			||||||
@@ -188,7 +196,7 @@ export class AssetService {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  public async serveFile(authUser: AuthUserDto, query: ServeFileDto, res: Res, headers: any) {
 | 
					  public async serveFile(authUser: AuthUserDto, query: ServeFileDto, res: Res, headers: any) {
 | 
				
			||||||
    let fileReadStream: ReadStream;
 | 
					    let fileReadStream: ReadStream;
 | 
				
			||||||
    const asset = await this.findOne(query.did, query.aid);
 | 
					    const asset = await this.findAssetOfDevice(query.did, query.aid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!asset) {
 | 
					    if (!asset) {
 | 
				
			||||||
      throw new NotFoundException('Asset does not exist');
 | 
					      throw new NotFoundException('Asset does not exist');
 | 
				
			||||||
@@ -258,12 +266,13 @@ export class AssetService {
 | 
				
			|||||||
      try {
 | 
					      try {
 | 
				
			||||||
        // Handle Video
 | 
					        // Handle Video
 | 
				
			||||||
        let videoPath = asset.originalPath;
 | 
					        let videoPath = asset.originalPath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mimeType = asset.mimeType;
 | 
					        let mimeType = asset.mimeType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await fs.access(videoPath, constants.R_OK | constants.W_OK);
 | 
					        await fs.access(videoPath, constants.R_OK | constants.W_OK);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (query.isWeb && asset.mimeType == 'video/quicktime') {
 | 
					        if (query.isWeb && asset.mimeType == 'video/quicktime') {
 | 
				
			||||||
          videoPath = asset.encodedVideoPath == '' ? asset.originalPath : asset.encodedVideoPath;
 | 
					          videoPath = asset.encodedVideoPath == '' ? String(asset.originalPath) : String(asset.encodedVideoPath);
 | 
				
			||||||
          mimeType = asset.encodedVideoPath == '' ? asset.mimeType : 'video/mp4';
 | 
					          mimeType = asset.encodedVideoPath == '' ? asset.mimeType : 'video/mp4';
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -390,7 +399,7 @@ export class AssetService {
 | 
				
			|||||||
    return Array.from(possibleSearchTerm).filter((x) => x != null);
 | 
					    return Array.from(possibleSearchTerm).filter((x) => x != null);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async searchAsset(authUser: AuthUserDto, searchAssetDto: SearchAssetDto) {
 | 
					  async searchAsset(authUser: AuthUserDto, searchAssetDto: SearchAssetDto): Promise<AssetResponseDto[]> {
 | 
				
			||||||
    const query = `
 | 
					    const query = `
 | 
				
			||||||
    SELECT a.*
 | 
					    SELECT a.*
 | 
				
			||||||
    FROM assets a
 | 
					    FROM assets a
 | 
				
			||||||
@@ -406,7 +415,12 @@ export class AssetService {
 | 
				
			|||||||
        );
 | 
					        );
 | 
				
			||||||
    `;
 | 
					    `;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return await this.assetRepository.query(query, [authUser.id, searchAssetDto.searchTerm]);
 | 
					    const searchResults: AssetEntity[] = await this.assetRepository.query(query, [
 | 
				
			||||||
 | 
					      authUser.id,
 | 
				
			||||||
 | 
					      searchAssetDto.searchTerm,
 | 
				
			||||||
 | 
					    ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return searchResults.map((asset) => mapAsset(asset));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async getCuratedLocation(authUser: AuthUserDto) {
 | 
					  async getCuratedLocation(authUser: AuthUserDto) {
 | 
				
			||||||
@@ -423,8 +437,8 @@ export class AssetService {
 | 
				
			|||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async getCuratedObject(authUser: AuthUserDto) {
 | 
					  async getCuratedObject(authUser: AuthUserDto): Promise<CuratedObjectsResponseDto[]> {
 | 
				
			||||||
    return await this.assetRepository.query(
 | 
					    const curatedObjects: CuratedObjectsResponseDto[] = await this.assetRepository.query(
 | 
				
			||||||
      `
 | 
					      `
 | 
				
			||||||
        SELECT DISTINCT ON (unnest(si.objects)) a.id, unnest(si.objects) as "object", a."resizePath", a."deviceAssetId", a."deviceId"
 | 
					        SELECT DISTINCT ON (unnest(si.objects)) a.id, unnest(si.objects) as "object", a."resizePath", a."deviceAssetId", a."deviceId"
 | 
				
			||||||
        FROM assets a
 | 
					        FROM assets a
 | 
				
			||||||
@@ -434,9 +448,11 @@ export class AssetService {
 | 
				
			|||||||
      `,
 | 
					      `,
 | 
				
			||||||
      [authUser.id],
 | 
					      [authUser.id],
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return curatedObjects;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async checkDuplicatedAsset(authUser: AuthUserDto, checkDuplicateAssetDto: CheckDuplicateAssetDto) {
 | 
					  async checkDuplicatedAsset(authUser: AuthUserDto, checkDuplicateAssetDto: CheckDuplicateAssetDto): Promise<boolean> {
 | 
				
			||||||
    const res = await this.assetRepository.findOne({
 | 
					    const res = await this.assetRepository.findOne({
 | 
				
			||||||
      where: {
 | 
					      where: {
 | 
				
			||||||
        deviceAssetId: checkDuplicateAssetDto.deviceAssetId,
 | 
					        deviceAssetId: checkDuplicateAssetDto.deviceAssetId,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,17 @@
 | 
				
			|||||||
 | 
					import { ApiProperty } from '@nestjs/swagger';
 | 
				
			||||||
import { IsNotEmpty } from 'class-validator';
 | 
					import { IsNotEmpty } from 'class-validator';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class DeleteAssetDto {
 | 
					export class DeleteAssetDto {
 | 
				
			||||||
  @IsNotEmpty()
 | 
					  @IsNotEmpty()
 | 
				
			||||||
 | 
					  @ApiProperty({
 | 
				
			||||||
 | 
					    isArray: true,
 | 
				
			||||||
 | 
					    type: String,
 | 
				
			||||||
 | 
					    title: 'Array of asset IDs to delete',
 | 
				
			||||||
 | 
					    example: [
 | 
				
			||||||
 | 
					      'bf973405-3f2a-48d2-a687-2ed4167164be',
 | 
				
			||||||
 | 
					      'dd41870b-5d00-46d2-924e-1d8489a0aa0f',
 | 
				
			||||||
 | 
					      'fad77c3f-deef-4e7e-9608-14c1aa4e559a',
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
  ids!: string[];
 | 
					  ids!: string[];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +1,13 @@
 | 
				
			|||||||
 | 
					import { ApiProperty } from '@nestjs/swagger';
 | 
				
			||||||
import { IsBooleanString, IsNotEmpty, IsOptional } from 'class-validator';
 | 
					import { IsBooleanString, IsNotEmpty, IsOptional } from 'class-validator';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class ServeFileDto {
 | 
					export class ServeFileDto {
 | 
				
			||||||
  //assetId
 | 
					 | 
				
			||||||
  @IsNotEmpty()
 | 
					  @IsNotEmpty()
 | 
				
			||||||
 | 
					  @ApiProperty({ type: String, title: 'Device Asset ID' })
 | 
				
			||||||
  aid!: string;
 | 
					  aid!: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  //deviceId
 | 
					 | 
				
			||||||
  @IsNotEmpty()
 | 
					  @IsNotEmpty()
 | 
				
			||||||
 | 
					  @ApiProperty({ type: String, title: 'Device ID' })
 | 
				
			||||||
  did!: string;
 | 
					  did!: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @IsOptional()
 | 
					  @IsOptional()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,19 +2,21 @@ import { AssetEntity, AssetType } from '@app/database/entities/asset.entity';
 | 
				
			|||||||
import { ExifResponseDto, mapExif } from './exif-response.dto';
 | 
					import { ExifResponseDto, mapExif } from './exif-response.dto';
 | 
				
			||||||
import { SmartInfoResponseDto, mapSmartInfo } from './smart-info-response.dto';
 | 
					import { SmartInfoResponseDto, mapSmartInfo } from './smart-info-response.dto';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface AssetResponseDto {
 | 
					export class AssetResponseDto {
 | 
				
			||||||
  id: string;
 | 
					  id!: string;
 | 
				
			||||||
  deviceAssetId: string;
 | 
					  deviceAssetId!: string;
 | 
				
			||||||
  ownerId: string;
 | 
					  ownerId!: string;
 | 
				
			||||||
  deviceId: string;
 | 
					  deviceId!: string;
 | 
				
			||||||
  type: AssetType;
 | 
					  type!: AssetType;
 | 
				
			||||||
  originalPath: string;
 | 
					  originalPath!: string;
 | 
				
			||||||
  resizePath: string | null;
 | 
					  resizePath!: string | null;
 | 
				
			||||||
  createdAt: string;
 | 
					  createdAt!: string;
 | 
				
			||||||
  modifiedAt: string;
 | 
					  modifiedAt!: string;
 | 
				
			||||||
  isFavorite: boolean;
 | 
					  isFavorite!: boolean;
 | 
				
			||||||
  mimeType: string | null;
 | 
					  mimeType!: string | null;
 | 
				
			||||||
  duration: string;
 | 
					  duration!: string;
 | 
				
			||||||
 | 
					  webpPath!: string | null;
 | 
				
			||||||
 | 
					  encodedVideoPath!: string | null;
 | 
				
			||||||
  exifInfo?: ExifResponseDto;
 | 
					  exifInfo?: ExifResponseDto;
 | 
				
			||||||
  smartInfo?: SmartInfoResponseDto;
 | 
					  smartInfo?: SmartInfoResponseDto;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -32,6 +34,8 @@ export function mapAsset(entity: AssetEntity): AssetResponseDto {
 | 
				
			|||||||
    modifiedAt: entity.modifiedAt,
 | 
					    modifiedAt: entity.modifiedAt,
 | 
				
			||||||
    isFavorite: entity.isFavorite,
 | 
					    isFavorite: entity.isFavorite,
 | 
				
			||||||
    mimeType: entity.mimeType,
 | 
					    mimeType: entity.mimeType,
 | 
				
			||||||
 | 
					    webpPath: entity.webpPath,
 | 
				
			||||||
 | 
					    encodedVideoPath: entity.encodedVideoPath,
 | 
				
			||||||
    duration: entity.duration ?? '0:00:00.00000',
 | 
					    duration: entity.duration ?? '0:00:00.00000',
 | 
				
			||||||
    exifInfo: entity.exifInfo ? mapExif(entity.exifInfo) : undefined,
 | 
					    exifInfo: entity.exifInfo ? mapExif(entity.exifInfo) : undefined,
 | 
				
			||||||
    smartInfo: entity.smartInfo ? mapSmartInfo(entity.smartInfo) : undefined,
 | 
					    smartInfo: entity.smartInfo ? mapSmartInfo(entity.smartInfo) : undefined,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					export class CuratedLocationsResponseDto {
 | 
				
			||||||
 | 
					  id!: string;
 | 
				
			||||||
 | 
					  city!: string;
 | 
				
			||||||
 | 
					  resizePath!: string;
 | 
				
			||||||
 | 
					  deviceAssetId!: string;
 | 
				
			||||||
 | 
					  deviceId!: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					export class CuratedObjectsResponseDto {
 | 
				
			||||||
 | 
					  id!: string;
 | 
				
			||||||
 | 
					  object!: string;
 | 
				
			||||||
 | 
					  resizePath!: string;
 | 
				
			||||||
 | 
					  deviceAssetId!: string;
 | 
				
			||||||
 | 
					  deviceId!: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,26 +1,26 @@
 | 
				
			|||||||
import { ExifEntity } from '@app/database/entities/exif.entity';
 | 
					import { ExifEntity } from '@app/database/entities/exif.entity';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface ExifResponseDto {
 | 
					export class ExifResponseDto {
 | 
				
			||||||
  id: string;
 | 
					  id!: string;
 | 
				
			||||||
  make: string | null;
 | 
					  make: string | null = null;
 | 
				
			||||||
  model: string | null;
 | 
					  model: string | null = null;
 | 
				
			||||||
  imageName: string | null;
 | 
					  imageName: string | null = null;
 | 
				
			||||||
  exifImageWidth: number | null;
 | 
					  exifImageWidth: number | null = null;
 | 
				
			||||||
  exifImageHeight: number | null;
 | 
					  exifImageHeight: number | null = null;
 | 
				
			||||||
  fileSizeInByte: number | null;
 | 
					  fileSizeInByte: number | null = null;
 | 
				
			||||||
  orientation: string | null;
 | 
					  orientation: string | null = null;
 | 
				
			||||||
  dateTimeOriginal: Date | null;
 | 
					  dateTimeOriginal: Date | null = null;
 | 
				
			||||||
  modifyDate: Date | null;
 | 
					  modifyDate: Date | null = null;
 | 
				
			||||||
  lensModel: string | null;
 | 
					  lensModel: string | null = null;
 | 
				
			||||||
  fNumber: number | null;
 | 
					  fNumber: number | null = null;
 | 
				
			||||||
  focalLength: number | null;
 | 
					  focalLength: number | null = null;
 | 
				
			||||||
  iso: number | null;
 | 
					  iso: number | null = null;
 | 
				
			||||||
  exposureTime: number | null;
 | 
					  exposureTime: number | null = null;
 | 
				
			||||||
  latitude: number | null;
 | 
					  latitude: number | null = null;
 | 
				
			||||||
  longitude: number | null;
 | 
					  longitude: number | null = null;
 | 
				
			||||||
  city: string | null;
 | 
					  city: string | null = null;
 | 
				
			||||||
  state: string | null;
 | 
					  state: string | null = null;
 | 
				
			||||||
  country: string | null;
 | 
					  country: string | null = null;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function mapExif(entity: ExifEntity): ExifResponseDto {
 | 
					export function mapExif(entity: ExifEntity): ExifResponseDto {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,9 @@
 | 
				
			|||||||
import { SmartInfoEntity } from '@app/database/entities/smart-info.entity';
 | 
					import { SmartInfoEntity } from '@app/database/entities/smart-info.entity';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface SmartInfoResponseDto {
 | 
					export class SmartInfoResponseDto {
 | 
				
			||||||
  id: string;
 | 
					  id?: string;
 | 
				
			||||||
  tags: string[] | null;
 | 
					  tags?: string[] | null;
 | 
				
			||||||
  objects: string[] | null;
 | 
					  objects?: string[] | null;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function mapSmartInfo(entity: SmartInfoEntity): SmartInfoResponseDto {
 | 
					export function mapSmartInfo(entity: SmartInfoEntity): SmartInfoResponseDto {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,30 +1,42 @@
 | 
				
			|||||||
import { Body, Controller, Post, UseGuards, ValidationPipe } from '@nestjs/common';
 | 
					import { Body, Controller, Post, UseGuards, ValidationPipe } from '@nestjs/common';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  ApiBadRequestResponse,
 | 
				
			||||||
 | 
					  ApiBearerAuth,
 | 
				
			||||||
 | 
					  ApiBody,
 | 
				
			||||||
 | 
					  ApiCreatedResponse,
 | 
				
			||||||
 | 
					  ApiResponse,
 | 
				
			||||||
 | 
					  ApiTags,
 | 
				
			||||||
 | 
					} from '@nestjs/swagger';
 | 
				
			||||||
import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
 | 
					import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
 | 
				
			||||||
import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard';
 | 
					import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard';
 | 
				
			||||||
import { AuthService } from './auth.service';
 | 
					import { AuthService } from './auth.service';
 | 
				
			||||||
import { LoginCredentialDto } from './dto/login-credential.dto';
 | 
					import { LoginCredentialDto } from './dto/login-credential.dto';
 | 
				
			||||||
 | 
					import { LoginResponseDto } from './response-dto/login-response.dto';
 | 
				
			||||||
import { SignUpDto } from './dto/sign-up.dto';
 | 
					import { SignUpDto } from './dto/sign-up.dto';
 | 
				
			||||||
 | 
					import { AdminSignupResponseDto } from './response-dto/admin-signup-response.dto';
 | 
				
			||||||
 | 
					import { ValidateAccessTokenResponseDto } from './response-dto/validate-asset-token-response.dto,';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@ApiTags('Authentication')
 | 
				
			||||||
@Controller('auth')
 | 
					@Controller('auth')
 | 
				
			||||||
export class AuthController {
 | 
					export class AuthController {
 | 
				
			||||||
  constructor(private readonly authService: AuthService) {}
 | 
					  constructor(private readonly authService: AuthService) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Post('/login')
 | 
					  @Post('/login')
 | 
				
			||||||
  async login(@Body(ValidationPipe) loginCredential: LoginCredentialDto) {
 | 
					  async login(@Body(ValidationPipe) loginCredential: LoginCredentialDto): Promise<LoginResponseDto> {
 | 
				
			||||||
    return await this.authService.login(loginCredential);
 | 
					    return await this.authService.login(loginCredential);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Post('/admin-sign-up')
 | 
					  @Post('/admin-sign-up')
 | 
				
			||||||
  async adminSignUp(@Body(ValidationPipe) signUpCrendential: SignUpDto) {
 | 
					  @ApiBadRequestResponse({ description: 'The server already has an admin' })
 | 
				
			||||||
    return await this.authService.adminSignUp(signUpCrendential);
 | 
					  async adminSignUp(@Body(ValidationPipe) signUpCredential: SignUpDto): Promise<AdminSignupResponseDto> {
 | 
				
			||||||
 | 
					    return await this.authService.adminSignUp(signUpCredential);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @UseGuards(JwtAuthGuard)
 | 
					  @UseGuards(JwtAuthGuard)
 | 
				
			||||||
 | 
					  @ApiBearerAuth()
 | 
				
			||||||
  @Post('/validateToken')
 | 
					  @Post('/validateToken')
 | 
				
			||||||
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
					  // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
				
			||||||
  async validateToken(@GetAuthUser() authUser: AuthUserDto) {
 | 
					  async validateAccessToken(@GetAuthUser() authUser: AuthUserDto): Promise<ValidateAccessTokenResponseDto> {
 | 
				
			||||||
    return {
 | 
					    return new ValidateAccessTokenResponseDto(true);
 | 
				
			||||||
      authStatus: true,
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,8 @@ import { ImmichJwtService } from '../../modules/immich-jwt/immich-jwt.service';
 | 
				
			|||||||
import { JwtPayloadDto } from './dto/jwt-payload.dto';
 | 
					import { JwtPayloadDto } from './dto/jwt-payload.dto';
 | 
				
			||||||
import { SignUpDto } from './dto/sign-up.dto';
 | 
					import { SignUpDto } from './dto/sign-up.dto';
 | 
				
			||||||
import * as bcrypt from 'bcrypt';
 | 
					import * as bcrypt from 'bcrypt';
 | 
				
			||||||
import { mapUser, UserResponseDto } from '../user/response-dto/user-response.dto';
 | 
					import { LoginResponseDto, mapLoginResponse } from './response-dto/login-response.dto';
 | 
				
			||||||
 | 
					import { AdminSignupResponseDto, mapAdminSignupResponse } from './response-dto/admin-signup-response.dto';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Injectable()
 | 
					@Injectable()
 | 
				
			||||||
export class AuthService {
 | 
					export class AuthService {
 | 
				
			||||||
@@ -49,7 +50,7 @@ export class AuthService {
 | 
				
			|||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public async login(loginCredential: LoginCredentialDto) {
 | 
					  public async login(loginCredential: LoginCredentialDto): Promise<LoginResponseDto> {
 | 
				
			||||||
    const validatedUser = await this.validateUser(loginCredential);
 | 
					    const validatedUser = await this.validateUser(loginCredential);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!validatedUser) {
 | 
					    if (!validatedUser) {
 | 
				
			||||||
@@ -57,20 +58,12 @@ export class AuthService {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const payload = new JwtPayloadDto(validatedUser.id, validatedUser.email);
 | 
					    const payload = new JwtPayloadDto(validatedUser.id, validatedUser.email);
 | 
				
			||||||
 | 
					    const accessToken = await this.immichJwtService.generateToken(payload);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return {
 | 
					    return mapLoginResponse(validatedUser, accessToken);
 | 
				
			||||||
      accessToken: await this.immichJwtService.generateToken(payload),
 | 
					 | 
				
			||||||
      userId: validatedUser.id,
 | 
					 | 
				
			||||||
      userEmail: validatedUser.email,
 | 
					 | 
				
			||||||
      firstName: validatedUser.firstName,
 | 
					 | 
				
			||||||
      lastName: validatedUser.lastName,
 | 
					 | 
				
			||||||
      isAdmin: validatedUser.isAdmin,
 | 
					 | 
				
			||||||
      profileImagePath: validatedUser.profileImagePath,
 | 
					 | 
				
			||||||
      shouldChangePassword: validatedUser.shouldChangePassword,
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public async adminSignUp(signUpCredential: SignUpDto): Promise<UserResponseDto> {
 | 
					  public async adminSignUp(signUpCredential: SignUpDto): Promise<AdminSignupResponseDto> {
 | 
				
			||||||
    const adminUser = await this.userRepository.findOne({ where: { isAdmin: true } });
 | 
					    const adminUser = await this.userRepository.findOne({ where: { isAdmin: true } });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (adminUser) {
 | 
					    if (adminUser) {
 | 
				
			||||||
@@ -88,7 +81,7 @@ export class AuthService {
 | 
				
			|||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const savedNewAdminUserUser = await this.userRepository.save(newAdminUser);
 | 
					      const savedNewAdminUserUser = await this.userRepository.save(newAdminUser);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return mapUser(savedNewAdminUserUser);
 | 
					      return mapAdminSignupResponse(savedNewAdminUserUser);
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      Logger.error('e', 'signUp');
 | 
					      Logger.error('e', 'signUp');
 | 
				
			||||||
      throw new InternalServerErrorException('Failed to register new admin user');
 | 
					      throw new InternalServerErrorException('Failed to register new admin user');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,12 @@
 | 
				
			|||||||
 | 
					import { ApiProperty } from '@nestjs/swagger';
 | 
				
			||||||
import { IsNotEmpty } from 'class-validator';
 | 
					import { IsNotEmpty } from 'class-validator';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class LoginCredentialDto {
 | 
					export class LoginCredentialDto {
 | 
				
			||||||
  @IsNotEmpty()
 | 
					  @IsNotEmpty()
 | 
				
			||||||
 | 
					  @ApiProperty({ example: 'testuser@email.com' })
 | 
				
			||||||
  email!: string;
 | 
					  email!: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @IsNotEmpty()
 | 
					  @IsNotEmpty()
 | 
				
			||||||
 | 
					  @ApiProperty({ example: 'password' })
 | 
				
			||||||
  password!: string;
 | 
					  password!: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +1,20 @@
 | 
				
			|||||||
 | 
					import { ApiProperty } from '@nestjs/swagger';
 | 
				
			||||||
import { IsNotEmpty } from 'class-validator';
 | 
					import { IsNotEmpty } from 'class-validator';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class SignUpDto {
 | 
					export class SignUpDto {
 | 
				
			||||||
  @IsNotEmpty()
 | 
					  @IsNotEmpty()
 | 
				
			||||||
 | 
					  @ApiProperty({ example: 'testuser@email.com' })
 | 
				
			||||||
  email!: string;
 | 
					  email!: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @IsNotEmpty()
 | 
					  @IsNotEmpty()
 | 
				
			||||||
 | 
					  @ApiProperty({ example: 'password' })
 | 
				
			||||||
  password!: string;
 | 
					  password!: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @IsNotEmpty()
 | 
					  @IsNotEmpty()
 | 
				
			||||||
 | 
					  @ApiProperty({ example: 'Admin' })
 | 
				
			||||||
  firstName!: string;
 | 
					  firstName!: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @IsNotEmpty()
 | 
					  @IsNotEmpty()
 | 
				
			||||||
 | 
					  @ApiProperty({ example: 'Doe' })
 | 
				
			||||||
  lastName!: string;
 | 
					  lastName!: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					import { UserEntity } from '@app/database/entities/user.entity';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class AdminSignupResponseDto {
 | 
				
			||||||
 | 
					  id!: string;
 | 
				
			||||||
 | 
					  email!: string;
 | 
				
			||||||
 | 
					  firstName!: string;
 | 
				
			||||||
 | 
					  lastName!: string;
 | 
				
			||||||
 | 
					  createdAt!: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function mapAdminSignupResponse(entity: UserEntity): AdminSignupResponseDto {
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    id: entity.id,
 | 
				
			||||||
 | 
					    email: entity.email,
 | 
				
			||||||
 | 
					    firstName: entity.firstName,
 | 
				
			||||||
 | 
					    lastName: entity.lastName,
 | 
				
			||||||
 | 
					    createdAt: entity.createdAt,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					import { UserEntity } from '@app/database/entities/user.entity';
 | 
				
			||||||
 | 
					import { ApiProperty, ApiResponseProperty } from '@nestjs/swagger';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class LoginResponseDto {
 | 
				
			||||||
 | 
					  @ApiResponseProperty()
 | 
				
			||||||
 | 
					  accessToken!: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @ApiResponseProperty()
 | 
				
			||||||
 | 
					  userId!: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @ApiResponseProperty()
 | 
				
			||||||
 | 
					  userEmail!: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @ApiResponseProperty()
 | 
				
			||||||
 | 
					  firstName!: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @ApiResponseProperty()
 | 
				
			||||||
 | 
					  lastName!: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @ApiResponseProperty()
 | 
				
			||||||
 | 
					  profileImagePath!: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @ApiResponseProperty()
 | 
				
			||||||
 | 
					  isAdmin!: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @ApiResponseProperty()
 | 
				
			||||||
 | 
					  shouldChangePassword!: boolean;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function mapLoginResponse(entity: UserEntity, accessToken: string): LoginResponseDto {
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    accessToken: accessToken,
 | 
				
			||||||
 | 
					    userId: entity.id,
 | 
				
			||||||
 | 
					    userEmail: entity.email,
 | 
				
			||||||
 | 
					    firstName: entity.firstName,
 | 
				
			||||||
 | 
					    lastName: entity.lastName,
 | 
				
			||||||
 | 
					    isAdmin: entity.isAdmin,
 | 
				
			||||||
 | 
					    profileImagePath: entity.profileImagePath,
 | 
				
			||||||
 | 
					    shouldChangePassword: entity.shouldChangePassword,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					export class ValidateAccessTokenResponseDto {
 | 
				
			||||||
 | 
					  constructor(authStatus: boolean) {
 | 
				
			||||||
 | 
					    this.authStatus = authStatus;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  authStatus: boolean;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,22 +1,32 @@
 | 
				
			|||||||
import { Controller, Post, Body, Patch, UseGuards, ValidationPipe } from '@nestjs/common';
 | 
					import { Controller, Post, Body, Patch, UseGuards, ValidationPipe } from '@nestjs/common';
 | 
				
			||||||
 | 
					import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
 | 
				
			||||||
import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
 | 
					import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
 | 
				
			||||||
import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard';
 | 
					import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard';
 | 
				
			||||||
import { DeviceInfoService } from './device-info.service';
 | 
					import { DeviceInfoService } from './device-info.service';
 | 
				
			||||||
import { CreateDeviceInfoDto } from './dto/create-device-info.dto';
 | 
					import { CreateDeviceInfoDto } from './dto/create-device-info.dto';
 | 
				
			||||||
import { UpdateDeviceInfoDto } from './dto/update-device-info.dto';
 | 
					import { UpdateDeviceInfoDto } from './dto/update-device-info.dto';
 | 
				
			||||||
 | 
					import { DeviceInfoResponseDto } from './response-dto/create-device-info-response.dto';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@UseGuards(JwtAuthGuard)
 | 
					@UseGuards(JwtAuthGuard)
 | 
				
			||||||
 | 
					@ApiBearerAuth()
 | 
				
			||||||
 | 
					@ApiTags('Device Info')
 | 
				
			||||||
@Controller('device-info')
 | 
					@Controller('device-info')
 | 
				
			||||||
export class DeviceInfoController {
 | 
					export class DeviceInfoController {
 | 
				
			||||||
  constructor(private readonly deviceInfoService: DeviceInfoService) {}
 | 
					  constructor(private readonly deviceInfoService: DeviceInfoService) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Post()
 | 
					  @Post()
 | 
				
			||||||
  async create(@Body(ValidationPipe) createDeviceInfoDto: CreateDeviceInfoDto, @GetAuthUser() authUser: AuthUserDto) {
 | 
					  async createDeviceInfo(
 | 
				
			||||||
    return await this.deviceInfoService.create(createDeviceInfoDto, authUser);
 | 
					    @Body(ValidationPipe) createDeviceInfoDto: CreateDeviceInfoDto,
 | 
				
			||||||
 | 
					    @GetAuthUser() authUser: AuthUserDto,
 | 
				
			||||||
 | 
					  ): Promise<DeviceInfoResponseDto> {
 | 
				
			||||||
 | 
					    return this.deviceInfoService.create(createDeviceInfoDto, authUser);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Patch()
 | 
					  @Patch()
 | 
				
			||||||
  async update(@Body(ValidationPipe) updateDeviceInfoDto: UpdateDeviceInfoDto, @GetAuthUser() authUser: AuthUserDto) {
 | 
					  async updateDeviceInfo(
 | 
				
			||||||
 | 
					    @Body(ValidationPipe) updateDeviceInfoDto: UpdateDeviceInfoDto,
 | 
				
			||||||
 | 
					    @GetAuthUser() authUser: AuthUserDto,
 | 
				
			||||||
 | 
					  ): Promise<DeviceInfoResponseDto> {
 | 
				
			||||||
    return this.deviceInfoService.update(authUser.id, updateDeviceInfoDto);
 | 
					    return this.deviceInfoService.update(authUser.id, updateDeviceInfoDto);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,11 @@
 | 
				
			|||||||
import { BadRequestException, Injectable, Logger } from '@nestjs/common';
 | 
					import { BadRequestException, Injectable, Logger, NotFoundException } from '@nestjs/common';
 | 
				
			||||||
import { InjectRepository } from '@nestjs/typeorm';
 | 
					import { InjectRepository } from '@nestjs/typeorm';
 | 
				
			||||||
import { Repository } from 'typeorm';
 | 
					import { Repository } from 'typeorm';
 | 
				
			||||||
import { AuthUserDto } from '../../decorators/auth-user.decorator';
 | 
					import { AuthUserDto } from '../../decorators/auth-user.decorator';
 | 
				
			||||||
import { CreateDeviceInfoDto } from './dto/create-device-info.dto';
 | 
					import { CreateDeviceInfoDto } from './dto/create-device-info.dto';
 | 
				
			||||||
import { UpdateDeviceInfoDto } from './dto/update-device-info.dto';
 | 
					import { UpdateDeviceInfoDto } from './dto/update-device-info.dto';
 | 
				
			||||||
import { DeviceInfoEntity } from '@app/database/entities/device-info.entity';
 | 
					import { DeviceInfoEntity } from '@app/database/entities/device-info.entity';
 | 
				
			||||||
 | 
					import { DeviceInfoResponseDto, mapDeviceInfoResponse } from './response-dto/create-device-info-response.dto';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Injectable()
 | 
					@Injectable()
 | 
				
			||||||
export class DeviceInfoService {
 | 
					export class DeviceInfoService {
 | 
				
			||||||
@@ -13,7 +14,7 @@ export class DeviceInfoService {
 | 
				
			|||||||
    private deviceRepository: Repository<DeviceInfoEntity>,
 | 
					    private deviceRepository: Repository<DeviceInfoEntity>,
 | 
				
			||||||
  ) {}
 | 
					  ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async create(createDeviceInfoDto: CreateDeviceInfoDto, authUser: AuthUserDto) {
 | 
					  async create(createDeviceInfoDto: CreateDeviceInfoDto, authUser: AuthUserDto): Promise<DeviceInfoResponseDto> {
 | 
				
			||||||
    const res = await this.deviceRepository.findOne({
 | 
					    const res = await this.deviceRepository.findOne({
 | 
				
			||||||
      where: {
 | 
					      where: {
 | 
				
			||||||
        deviceId: createDeviceInfoDto.deviceId,
 | 
					        deviceId: createDeviceInfoDto.deviceId,
 | 
				
			||||||
@@ -23,7 +24,7 @@ export class DeviceInfoService {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if (res) {
 | 
					    if (res) {
 | 
				
			||||||
      Logger.log('Device Info Exist', 'createDeviceInfo');
 | 
					      Logger.log('Device Info Exist', 'createDeviceInfo');
 | 
				
			||||||
      return res;
 | 
					      return mapDeviceInfoResponse(res);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const deviceInfo = new DeviceInfoEntity();
 | 
					    const deviceInfo = new DeviceInfoEntity();
 | 
				
			||||||
@@ -31,20 +32,18 @@ export class DeviceInfoService {
 | 
				
			|||||||
    deviceInfo.deviceType = createDeviceInfoDto.deviceType;
 | 
					    deviceInfo.deviceType = createDeviceInfoDto.deviceType;
 | 
				
			||||||
    deviceInfo.userId = authUser.id;
 | 
					    deviceInfo.userId = authUser.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try {
 | 
					    const newDeviceInfo = await this.deviceRepository.save(deviceInfo);
 | 
				
			||||||
      return await this.deviceRepository.save(deviceInfo);
 | 
					
 | 
				
			||||||
    } catch (e) {
 | 
					    return mapDeviceInfoResponse(newDeviceInfo);
 | 
				
			||||||
      Logger.error('Error creating new device info', 'createDeviceInfo');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async update(userId: string, updateDeviceInfoDto: UpdateDeviceInfoDto) {
 | 
					  async update(userId: string, updateDeviceInfoDto: UpdateDeviceInfoDto): Promise<DeviceInfoResponseDto> {
 | 
				
			||||||
    const deviceInfo = await this.deviceRepository.findOne({
 | 
					    const deviceInfo = await this.deviceRepository.findOne({
 | 
				
			||||||
      where: { deviceId: updateDeviceInfoDto.deviceId, userId: userId },
 | 
					      where: { deviceId: updateDeviceInfoDto.deviceId, userId: userId },
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!deviceInfo) {
 | 
					    if (!deviceInfo) {
 | 
				
			||||||
      throw new BadRequestException('Device Not Found');
 | 
					      throw new NotFoundException('Device Not Found');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const res = await this.deviceRepository.update(
 | 
					    const res = await this.deviceRepository.update(
 | 
				
			||||||
@@ -55,9 +54,15 @@ export class DeviceInfoService {
 | 
				
			|||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (res.affected == 1) {
 | 
					    if (res.affected == 1) {
 | 
				
			||||||
      return await this.deviceRepository.findOne({
 | 
					      const updatedDeviceInfo = await this.deviceRepository.findOne({
 | 
				
			||||||
        where: { deviceId: updateDeviceInfoDto.deviceId, userId: userId },
 | 
					        where: { deviceId: updateDeviceInfoDto.deviceId, userId: userId },
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (!updatedDeviceInfo) {
 | 
				
			||||||
 | 
					        throw new NotFoundException('Device Not Found');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return mapDeviceInfoResponse(updatedDeviceInfo);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      throw new BadRequestException('Bad Request');
 | 
					      throw new BadRequestException('Bad Request');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					import { DeviceInfoEntity, DeviceType } from '@app/database/entities/device-info.entity';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class DeviceInfoResponseDto {
 | 
				
			||||||
 | 
					  id!: number;
 | 
				
			||||||
 | 
					  userId!: string;
 | 
				
			||||||
 | 
					  deviceId!: string;
 | 
				
			||||||
 | 
					  deviceType!: DeviceType;
 | 
				
			||||||
 | 
					  notificationToken!: string | null;
 | 
				
			||||||
 | 
					  createdAt!: string;
 | 
				
			||||||
 | 
					  isAutoBackup!: boolean;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function mapDeviceInfoResponse(entity: DeviceInfoEntity): DeviceInfoResponseDto {
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    id: entity.id,
 | 
				
			||||||
 | 
					    userId: entity.userId,
 | 
				
			||||||
 | 
					    deviceId: entity.deviceId,
 | 
				
			||||||
 | 
					    deviceType: entity.deviceType,
 | 
				
			||||||
 | 
					    notificationToken: entity.notificationToken,
 | 
				
			||||||
 | 
					    createdAt: entity.createdAt,
 | 
				
			||||||
 | 
					    isAutoBackup: entity.isAutoBackup,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,4 @@
 | 
				
			|||||||
// TODO: this is being used as a response DTO. Should be changed to interface
 | 
					export class ServerInfoResponseDto {
 | 
				
			||||||
export class ServerInfoDto {
 | 
					 | 
				
			||||||
  diskSize!: string;
 | 
					  diskSize!: string;
 | 
				
			||||||
  diskUse!: string;
 | 
					  diskUse!: string;
 | 
				
			||||||
  diskAvailable!: string;
 | 
					  diskAvailable!: string;
 | 
				
			||||||
@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					import { ApiResponseProperty } from '@nestjs/swagger';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class ServerPingResponse {
 | 
				
			||||||
 | 
					  constructor(res: string) {
 | 
				
			||||||
 | 
					    this.res = res;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @ApiResponseProperty({ type: String, example: 'pong' })
 | 
				
			||||||
 | 
					  res!: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					import { IServerVersion } from 'apps/immich/src/constants/server_version.constant';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class ServerVersionReponseDto implements IServerVersion {
 | 
				
			||||||
 | 
					  major!: number;
 | 
				
			||||||
 | 
					  minor!: number;
 | 
				
			||||||
 | 
					  patch!: number;
 | 
				
			||||||
 | 
					  build!: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -3,34 +3,28 @@ import { ConfigService } from '@nestjs/config';
 | 
				
			|||||||
import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard';
 | 
					import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard';
 | 
				
			||||||
import { ServerInfoService } from './server-info.service';
 | 
					import { ServerInfoService } from './server-info.service';
 | 
				
			||||||
import { serverVersion } from '../../constants/server_version.constant';
 | 
					import { serverVersion } from '../../constants/server_version.constant';
 | 
				
			||||||
 | 
					import { ApiTags } from '@nestjs/swagger';
 | 
				
			||||||
 | 
					import { ServerPingResponse } from './response-dto/server-ping-response.dto';
 | 
				
			||||||
 | 
					import { ServerVersionReponseDto } from './response-dto/server-version-response.dto';
 | 
				
			||||||
 | 
					import { ServerInfoResponseDto } from './response-dto/server-info-response.dto';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@ApiTags('Server Info')
 | 
				
			||||||
@Controller('server-info')
 | 
					@Controller('server-info')
 | 
				
			||||||
export class ServerInfoController {
 | 
					export class ServerInfoController {
 | 
				
			||||||
  constructor(private readonly serverInfoService: ServerInfoService, private readonly configService: ConfigService) {}
 | 
					  constructor(private readonly serverInfoService: ServerInfoService, private readonly configService: ConfigService) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Get()
 | 
					  @Get()
 | 
				
			||||||
  async getServerInfo() {
 | 
					  async getServerInfo(): Promise<ServerInfoResponseDto> {
 | 
				
			||||||
    return await this.serverInfoService.getServerInfo();
 | 
					    return await this.serverInfoService.getServerInfo();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Get('/ping')
 | 
					  @Get('/ping')
 | 
				
			||||||
  async getServerPulse() {
 | 
					  async pingServer(): Promise<ServerPingResponse> {
 | 
				
			||||||
    return {
 | 
					    return new ServerPingResponse('pong');
 | 
				
			||||||
      res: 'pong',
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @UseGuards(JwtAuthGuard)
 | 
					 | 
				
			||||||
  @Get('/mapbox')
 | 
					 | 
				
			||||||
  async getMapboxInfo() {
 | 
					 | 
				
			||||||
    return {
 | 
					 | 
				
			||||||
      isEnable: this.configService.get('ENABLE_MAPBOX'),
 | 
					 | 
				
			||||||
      mapboxSecret: this.configService.get('MAPBOX_KEY'),
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Get('/version')
 | 
					  @Get('/version')
 | 
				
			||||||
  async getServerVersion() {
 | 
					  async getServerVersion(): Promise<ServerVersionReponseDto> {
 | 
				
			||||||
    return serverVersion;
 | 
					    return serverVersion;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
import { Injectable } from '@nestjs/common';
 | 
					import { Injectable } from '@nestjs/common';
 | 
				
			||||||
import { ServerInfoDto } from './dto/server-info.dto';
 | 
					import { ServerInfoResponseDto } from './response-dto/server-info-response.dto';
 | 
				
			||||||
import diskusage from 'diskusage';
 | 
					import diskusage from 'diskusage';
 | 
				
			||||||
import { APP_UPLOAD_LOCATION } from '../../constants/upload_location.constant';
 | 
					import { APP_UPLOAD_LOCATION } from '../../constants/upload_location.constant';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -10,7 +10,7 @@ export class ServerInfoService {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    const usagePercentage = (((diskInfo.total - diskInfo.free) / diskInfo.total) * 100).toFixed(2);
 | 
					    const usagePercentage = (((diskInfo.total - diskInfo.free) / diskInfo.total) * 100).toFixed(2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const serverInfo = new ServerInfoDto();
 | 
					    const serverInfo = new ServerInfoResponseDto();
 | 
				
			||||||
    serverInfo.diskAvailable = ServerInfoService.getHumanReadableString(diskInfo.available);
 | 
					    serverInfo.diskAvailable = ServerInfoService.getHumanReadableString(diskInfo.available);
 | 
				
			||||||
    serverInfo.diskSize = ServerInfoService.getHumanReadableString(diskInfo.total);
 | 
					    serverInfo.diskSize = ServerInfoService.getHumanReadableString(diskInfo.total);
 | 
				
			||||||
    serverInfo.diskUse = ServerInfoService.getHumanReadableString(diskInfo.total - diskInfo.free);
 | 
					    serverInfo.diskUse = ServerInfoService.getHumanReadableString(diskInfo.total - diskInfo.free);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					import { ApiProperty } from '@nestjs/swagger';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class CreateProfileImageDto {
 | 
				
			||||||
 | 
					  @ApiProperty({ type: 'string', format: 'binary' })
 | 
				
			||||||
 | 
					  file: any;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,27 +1,20 @@
 | 
				
			|||||||
 | 
					import { ApiProperty } from '@nestjs/swagger';
 | 
				
			||||||
import { IsNotEmpty, IsOptional } from 'class-validator';
 | 
					import { IsNotEmpty, IsOptional } from 'class-validator';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class CreateUserDto {
 | 
					export class CreateUserDto {
 | 
				
			||||||
  @IsNotEmpty()
 | 
					  @IsNotEmpty()
 | 
				
			||||||
 | 
					  @ApiProperty({ example: 'testuser@email.com' })
 | 
				
			||||||
  email!: string;
 | 
					  email!: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @IsNotEmpty()
 | 
					  @IsNotEmpty()
 | 
				
			||||||
 | 
					  @ApiProperty({ example: 'password' })
 | 
				
			||||||
  password!: string;
 | 
					  password!: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @IsNotEmpty()
 | 
					  @IsNotEmpty()
 | 
				
			||||||
 | 
					  @ApiProperty({ example: 'John' })
 | 
				
			||||||
  firstName!: string;
 | 
					  firstName!: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @IsNotEmpty()
 | 
					  @IsNotEmpty()
 | 
				
			||||||
 | 
					  @ApiProperty({ example: 'Doe' })
 | 
				
			||||||
  lastName!: string;
 | 
					  lastName!: string;
 | 
				
			||||||
 | 
					 | 
				
			||||||
  @IsOptional()
 | 
					 | 
				
			||||||
  profileImagePath?: string;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @IsOptional()
 | 
					 | 
				
			||||||
  isAdmin?: boolean;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @IsOptional()
 | 
					 | 
				
			||||||
  shouldChangePassword?: boolean;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @IsOptional()
 | 
					 | 
				
			||||||
  id?: string;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,24 @@
 | 
				
			|||||||
import { PartialType } from '@nestjs/mapped-types';
 | 
					import { IsNotEmpty, IsOptional } from 'class-validator';
 | 
				
			||||||
import { CreateUserDto } from './create-user.dto';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class UpdateUserDto extends PartialType(CreateUserDto) {}
 | 
					export class UpdateUserDto {
 | 
				
			||||||
 | 
					  @IsNotEmpty()
 | 
				
			||||||
 | 
					  id!: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  password?: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  firstName?: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  lastName?: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  isAdmin?: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  shouldChangePassword?: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  profileImagePath?: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					export class CreateProfileImageResponseDto {
 | 
				
			||||||
 | 
					  userId!: string;
 | 
				
			||||||
 | 
					  profileImagePath!: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function mapCreateProfileImageResponse(userId: string, profileImagePath: string): CreateProfileImageResponseDto {
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    userId: userId,
 | 
				
			||||||
 | 
					    profileImagePath: profileImagePath,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					export class UserCountResponseDto {
 | 
				
			||||||
 | 
					  userCount!: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function mapUserCountResponse(count: number): UserCountResponseDto {
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    userCount: count,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,11 +1,14 @@
 | 
				
			|||||||
import { UserEntity } from '../../../../../../libs/database/src/entities/user.entity';
 | 
					import { UserEntity } from '@app/database/entities/user.entity';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface UserResponseDto {
 | 
					export class UserResponseDto {
 | 
				
			||||||
  id: string;
 | 
					  id!: string;
 | 
				
			||||||
  email: string;
 | 
					  email!: string;
 | 
				
			||||||
  firstName: string;
 | 
					  firstName!: string;
 | 
				
			||||||
  lastName: string;
 | 
					  lastName!: string;
 | 
				
			||||||
  createdAt: string;
 | 
					  createdAt!: string;
 | 
				
			||||||
 | 
					  profileImagePath!: string;
 | 
				
			||||||
 | 
					  shouldChangePassword!: boolean;
 | 
				
			||||||
 | 
					  isAdmin!: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function mapUser(entity: UserEntity): UserResponseDto {
 | 
					export function mapUser(entity: UserEntity): UserResponseDto {
 | 
				
			||||||
@@ -15,5 +18,8 @@ export function mapUser(entity: UserEntity): UserResponseDto {
 | 
				
			|||||||
    firstName: entity.firstName,
 | 
					    firstName: entity.firstName,
 | 
				
			||||||
    lastName: entity.lastName,
 | 
					    lastName: entity.lastName,
 | 
				
			||||||
    createdAt: entity.createdAt,
 | 
					    createdAt: entity.createdAt,
 | 
				
			||||||
 | 
					    profileImagePath: entity.profileImagePath,
 | 
				
			||||||
 | 
					    shouldChangePassword: entity.shouldChangePassword,
 | 
				
			||||||
 | 
					    isAdmin: entity.isAdmin,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,7 @@ import {
 | 
				
			|||||||
  UseInterceptors,
 | 
					  UseInterceptors,
 | 
				
			||||||
  UploadedFile,
 | 
					  UploadedFile,
 | 
				
			||||||
  Response,
 | 
					  Response,
 | 
				
			||||||
 | 
					  StreamableFile,
 | 
				
			||||||
} from '@nestjs/common';
 | 
					} from '@nestjs/common';
 | 
				
			||||||
import { UserService } from './user.service';
 | 
					import { UserService } from './user.service';
 | 
				
			||||||
import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard';
 | 
					import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard';
 | 
				
			||||||
@@ -21,50 +22,72 @@ import { UpdateUserDto } from './dto/update-user.dto';
 | 
				
			|||||||
import { FileInterceptor } from '@nestjs/platform-express';
 | 
					import { FileInterceptor } from '@nestjs/platform-express';
 | 
				
			||||||
import { profileImageUploadOption } from '../../config/profile-image-upload.config';
 | 
					import { profileImageUploadOption } from '../../config/profile-image-upload.config';
 | 
				
			||||||
import { Response as Res } from 'express';
 | 
					import { Response as Res } from 'express';
 | 
				
			||||||
 | 
					import { ApiBearerAuth, ApiBody, ApiConsumes, ApiTags } from '@nestjs/swagger';
 | 
				
			||||||
 | 
					import { UserResponseDto } from './response-dto/user-response.dto';
 | 
				
			||||||
 | 
					import { UserEntity } from '@app/database/entities/user.entity';
 | 
				
			||||||
 | 
					import { UserCountResponseDto } from './response-dto/user-count-response.dto';
 | 
				
			||||||
 | 
					import { CreateProfileImageDto } from './dto/create-profile-image.dto';
 | 
				
			||||||
 | 
					import { CreateProfileImageResponseDto } from './response-dto/create-profile-image-response.dto';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@ApiTags('User')
 | 
				
			||||||
@Controller('user')
 | 
					@Controller('user')
 | 
				
			||||||
export class UserController {
 | 
					export class UserController {
 | 
				
			||||||
  constructor(private readonly userService: UserService) {}
 | 
					  constructor(private readonly userService: UserService) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @UseGuards(JwtAuthGuard)
 | 
					  @UseGuards(JwtAuthGuard)
 | 
				
			||||||
 | 
					  @ApiBearerAuth()
 | 
				
			||||||
  @Get()
 | 
					  @Get()
 | 
				
			||||||
  async getAllUsers(@GetAuthUser() authUser: AuthUserDto, @Query('isAll') isAll: boolean) {
 | 
					  async getAllUsers(@GetAuthUser() authUser: AuthUserDto, @Query('isAll') isAll: boolean): Promise<UserResponseDto[]> {
 | 
				
			||||||
    return await this.userService.getAllUsers(authUser, isAll);
 | 
					    return await this.userService.getAllUsers(authUser, isAll);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @UseGuards(JwtAuthGuard)
 | 
					  @UseGuards(JwtAuthGuard)
 | 
				
			||||||
 | 
					  @ApiBearerAuth()
 | 
				
			||||||
  @Get('me')
 | 
					  @Get('me')
 | 
				
			||||||
  async getUserInfo(@GetAuthUser() authUser: AuthUserDto) {
 | 
					  async getMyUserInfo(@GetAuthUser() authUser: AuthUserDto): Promise<UserResponseDto> {
 | 
				
			||||||
    return await this.userService.getUserInfo(authUser);
 | 
					    return await this.userService.getUserInfo(authUser);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @UseGuards(JwtAuthGuard)
 | 
					  @UseGuards(JwtAuthGuard)
 | 
				
			||||||
 | 
					  @ApiBearerAuth()
 | 
				
			||||||
  @UseGuards(AdminRolesGuard)
 | 
					  @UseGuards(AdminRolesGuard)
 | 
				
			||||||
  @Post()
 | 
					  @Post()
 | 
				
			||||||
  async createNewUser(@Body(ValidationPipe) createUserDto: CreateUserDto) {
 | 
					  async createUser(@Body(ValidationPipe) createUserDto: CreateUserDto): Promise<UserResponseDto> {
 | 
				
			||||||
    return await this.userService.createUser(createUserDto);
 | 
					    return await this.userService.createUser(createUserDto);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Get('/count')
 | 
					  @Get('/count')
 | 
				
			||||||
  async getUserCount(@Query('isAdmin') isAdmin: boolean) {
 | 
					  async getUserCount(@Query('isAdmin') isAdmin: boolean): Promise<UserCountResponseDto> {
 | 
				
			||||||
    return await this.userService.getUserCount(isAdmin);
 | 
					    return await this.userService.getUserCount(isAdmin);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @UseGuards(JwtAuthGuard)
 | 
					  @UseGuards(JwtAuthGuard)
 | 
				
			||||||
 | 
					  @ApiBearerAuth()
 | 
				
			||||||
  @Put()
 | 
					  @Put()
 | 
				
			||||||
  async updateUser(@Body(ValidationPipe) updateUserDto: UpdateUserDto) {
 | 
					  async updateUser(@Body(ValidationPipe) updateUserDto: UpdateUserDto): Promise<UserResponseDto> {
 | 
				
			||||||
    return await this.userService.updateUser(updateUserDto);
 | 
					    return await this.userService.updateUser(updateUserDto);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @UseGuards(JwtAuthGuard)
 | 
					 | 
				
			||||||
  @UseInterceptors(FileInterceptor('file', profileImageUploadOption))
 | 
					  @UseInterceptors(FileInterceptor('file', profileImageUploadOption))
 | 
				
			||||||
 | 
					  @UseGuards(JwtAuthGuard)
 | 
				
			||||||
 | 
					  @ApiBearerAuth()
 | 
				
			||||||
 | 
					  @ApiConsumes('multipart/form-data')
 | 
				
			||||||
 | 
					  @ApiBody({
 | 
				
			||||||
 | 
					    type: CreateProfileImageDto,
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
  @Post('/profile-image')
 | 
					  @Post('/profile-image')
 | 
				
			||||||
  async createProfileImage(@GetAuthUser() authUser: AuthUserDto, @UploadedFile() fileInfo: Express.Multer.File) {
 | 
					  async createProfileImage(
 | 
				
			||||||
 | 
					    @GetAuthUser() authUser: AuthUserDto,
 | 
				
			||||||
 | 
					    @UploadedFile() fileInfo: Express.Multer.File,
 | 
				
			||||||
 | 
					  ): Promise<CreateProfileImageResponseDto> {
 | 
				
			||||||
    return await this.userService.createProfileImage(authUser, fileInfo);
 | 
					    return await this.userService.createProfileImage(authUser, fileInfo);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Get('/profile-image/:userId')
 | 
					  @Get('/profile-image/:userId')
 | 
				
			||||||
  async getProfileImage(@Param('userId') userId: string, @Response({ passthrough: true }) res: Res) {
 | 
					  async getProfileImage(
 | 
				
			||||||
    return await this.userService.getUserProfileImage(userId, res);
 | 
					    @Param('userId') userId: string,
 | 
				
			||||||
 | 
					    @Response({ passthrough: true }) res: Res,
 | 
				
			||||||
 | 
					  ): Promise<StreamableFile | undefined> {
 | 
				
			||||||
 | 
					    return this.userService.getUserProfileImage(userId, res);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,6 +16,11 @@ import * as bcrypt from 'bcrypt';
 | 
				
			|||||||
import { createReadStream } from 'fs';
 | 
					import { createReadStream } from 'fs';
 | 
				
			||||||
import { Response as Res } from 'express';
 | 
					import { Response as Res } from 'express';
 | 
				
			||||||
import { mapUser, UserResponseDto } from './response-dto/user-response.dto';
 | 
					import { mapUser, UserResponseDto } from './response-dto/user-response.dto';
 | 
				
			||||||
 | 
					import { mapUserCountResponse, UserCountResponseDto } from './response-dto/user-count-response.dto';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  CreateProfileImageResponseDto,
 | 
				
			||||||
 | 
					  mapCreateProfileImageResponse,
 | 
				
			||||||
 | 
					} from './response-dto/create-profile-image-response.dto';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Injectable()
 | 
					@Injectable()
 | 
				
			||||||
export class UserService {
 | 
					export class UserService {
 | 
				
			||||||
@@ -24,24 +29,32 @@ export class UserService {
 | 
				
			|||||||
    private userRepository: Repository<UserEntity>,
 | 
					    private userRepository: Repository<UserEntity>,
 | 
				
			||||||
  ) {}
 | 
					  ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async getAllUsers(authUser: AuthUserDto, isAll: boolean) {
 | 
					  async getAllUsers(authUser: AuthUserDto, isAll: boolean): Promise<UserResponseDto[]> {
 | 
				
			||||||
    if (isAll) {
 | 
					    if (isAll) {
 | 
				
			||||||
      return await this.userRepository.find();
 | 
					      const allUsers = await this.userRepository.find();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return allUsers.map(mapUser);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return await this.userRepository.find({
 | 
					    const allUserExceptRequestedUser = await this.userRepository.find({
 | 
				
			||||||
      where: { id: Not(authUser.id) },
 | 
					      where: { id: Not(authUser.id) },
 | 
				
			||||||
      order: {
 | 
					      order: {
 | 
				
			||||||
        createdAt: 'DESC',
 | 
					        createdAt: 'DESC',
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return allUserExceptRequestedUser.map(mapUser);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async getUserInfo(authUser: AuthUserDto) {
 | 
					  async getUserInfo(authUser: AuthUserDto): Promise<UserResponseDto> {
 | 
				
			||||||
    return this.userRepository.findOne({ where: { id: authUser.id } });
 | 
					    const user = await this.userRepository.findOne({ where: { id: authUser.id } });
 | 
				
			||||||
 | 
					    if (!user) {
 | 
				
			||||||
 | 
					      throw new BadRequestException('User not found');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return mapUser(user);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async getUserCount(isAdmin: boolean) {
 | 
					  async getUserCount(isAdmin: boolean): Promise<UserCountResponseDto> {
 | 
				
			||||||
    let users;
 | 
					    let users;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (isAdmin) {
 | 
					    if (isAdmin) {
 | 
				
			||||||
@@ -50,9 +63,7 @@ export class UserService {
 | 
				
			|||||||
      users = await this.userRepository.find();
 | 
					      users = await this.userRepository.find();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return {
 | 
					    return mapUserCountResponse(users.length);
 | 
				
			||||||
      userCount: users.length,
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async createUser(createUserDto: CreateUserDto): Promise<UserResponseDto> {
 | 
					  async createUser(createUserDto: CreateUserDto): Promise<UserResponseDto> {
 | 
				
			||||||
@@ -84,7 +95,7 @@ export class UserService {
 | 
				
			|||||||
    return bcrypt.hash(password, salt);
 | 
					    return bcrypt.hash(password, salt);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async updateUser(updateUserDto: UpdateUserDto) {
 | 
					  async updateUser(updateUserDto: UpdateUserDto): Promise<UserResponseDto> {
 | 
				
			||||||
    const user = await this.userRepository.findOne({ where: { id: updateUserDto.id } });
 | 
					    const user = await this.userRepository.findOne({ where: { id: updateUserDto.id } });
 | 
				
			||||||
    if (!user) {
 | 
					    if (!user) {
 | 
				
			||||||
      throw new NotFoundException('User not found');
 | 
					      throw new NotFoundException('User not found');
 | 
				
			||||||
@@ -115,31 +126,23 @@ export class UserService {
 | 
				
			|||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const updatedUser = await this.userRepository.save(user);
 | 
					      const updatedUser = await this.userRepository.save(user);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // TODO: this should probably retrun UserResponseDto
 | 
					      return mapUser(updatedUser);
 | 
				
			||||||
      return {
 | 
					 | 
				
			||||||
        id: updatedUser.id,
 | 
					 | 
				
			||||||
        email: updatedUser.email,
 | 
					 | 
				
			||||||
        firstName: updatedUser.firstName,
 | 
					 | 
				
			||||||
        lastName: updatedUser.lastName,
 | 
					 | 
				
			||||||
        isAdmin: updatedUser.isAdmin,
 | 
					 | 
				
			||||||
        profileImagePath: updatedUser.profileImagePath,
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      Logger.error(e, 'Create new user');
 | 
					      Logger.error(e, 'Create new user');
 | 
				
			||||||
      throw new InternalServerErrorException('Failed to register new user');
 | 
					      throw new InternalServerErrorException('Failed to register new user');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async createProfileImage(authUser: AuthUserDto, fileInfo: Express.Multer.File) {
 | 
					  async createProfileImage(
 | 
				
			||||||
 | 
					    authUser: AuthUserDto,
 | 
				
			||||||
 | 
					    fileInfo: Express.Multer.File,
 | 
				
			||||||
 | 
					  ): Promise<CreateProfileImageResponseDto> {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      await this.userRepository.update(authUser.id, {
 | 
					      await this.userRepository.update(authUser.id, {
 | 
				
			||||||
        profileImagePath: fileInfo.path,
 | 
					        profileImagePath: fileInfo.path,
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return {
 | 
					      return mapCreateProfileImageResponse(authUser.id, fileInfo.path);
 | 
				
			||||||
        userId: authUser.id,
 | 
					 | 
				
			||||||
        profileImagePath: fileInfo.path,
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      Logger.error(e, 'Create User Profile Image');
 | 
					      Logger.error(e, 'Create User Profile Image');
 | 
				
			||||||
      throw new InternalServerErrorException('Failed to create new user profile image');
 | 
					      throw new InternalServerErrorException('Failed to create new user profile image');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,14 @@
 | 
				
			|||||||
// major.minor.patch+build
 | 
					// major.minor.patch+build
 | 
				
			||||||
// check mobile/pubspec.yml for current release version
 | 
					// check mobile/pubspec.yml for current release version
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const serverVersion = {
 | 
					export interface IServerVersion {
 | 
				
			||||||
 | 
					  major: number;
 | 
				
			||||||
 | 
					  minor: number;
 | 
				
			||||||
 | 
					  patch: number;
 | 
				
			||||||
 | 
					  build: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const serverVersion: IServerVersion = {
 | 
				
			||||||
  major: 1,
 | 
					  major: 1,
 | 
				
			||||||
  minor: 17,
 | 
					  minor: 17,
 | 
				
			||||||
  patch: 0,
 | 
					  patch: 0,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,9 @@
 | 
				
			|||||||
import { Logger } from '@nestjs/common';
 | 
					import { Logger } from '@nestjs/common';
 | 
				
			||||||
import { NestFactory } from '@nestjs/core';
 | 
					import { NestFactory } from '@nestjs/core';
 | 
				
			||||||
import { NestExpressApplication } from '@nestjs/platform-express';
 | 
					import { NestExpressApplication } from '@nestjs/platform-express';
 | 
				
			||||||
 | 
					import { DocumentBuilder, SwaggerDocumentOptions, SwaggerModule } from '@nestjs/swagger';
 | 
				
			||||||
 | 
					import { writeFileSync } from 'fs';
 | 
				
			||||||
 | 
					import path from 'path';
 | 
				
			||||||
import { AppModule } from './app.module';
 | 
					import { AppModule } from './app.module';
 | 
				
			||||||
import { RedisIoAdapter } from './middlewares/redis-io.adapter.middleware';
 | 
					import { RedisIoAdapter } from './middlewares/redis-io.adapter.middleware';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -15,6 +18,38 @@ async function bootstrap() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  app.useWebSocketAdapter(new RedisIoAdapter(app));
 | 
					  app.useWebSocketAdapter(new RedisIoAdapter(app));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const config = new DocumentBuilder()
 | 
				
			||||||
 | 
					    .setTitle('Immich')
 | 
				
			||||||
 | 
					    .setDescription('Immich API')
 | 
				
			||||||
 | 
					    .setVersion('1.17.0')
 | 
				
			||||||
 | 
					    .addBearerAuth({
 | 
				
			||||||
 | 
					      type: 'http',
 | 
				
			||||||
 | 
					      scheme: 'bearer',
 | 
				
			||||||
 | 
					      bearerFormat: 'JWT',
 | 
				
			||||||
 | 
					      name: 'JWT',
 | 
				
			||||||
 | 
					      description: 'Enter JWT token',
 | 
				
			||||||
 | 
					      in: 'header',
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    .addServer('/api')
 | 
				
			||||||
 | 
					    .build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const apiDocumentOptions: SwaggerDocumentOptions = {
 | 
				
			||||||
 | 
					    operationIdFactory: (controllerKey: string, methodKey: string) => methodKey,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const apiDocument = SwaggerModule.createDocument(app, config, apiDocumentOptions);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  SwaggerModule.setup('doc', app, apiDocument, {
 | 
				
			||||||
 | 
					    swaggerOptions: {
 | 
				
			||||||
 | 
					      persistAuthorization: true,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    customSiteTitle: 'Immich API Documentation',
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Generate API Documentation
 | 
				
			||||||
 | 
					  const outputPath = path.resolve(process.cwd(), 'immich-openapi-specs.json');
 | 
				
			||||||
 | 
					  writeFileSync(outputPath, JSON.stringify(apiDocument), { encoding: 'utf8' });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  await app.listen(3001, () => {
 | 
					  await app.listen(3001, () => {
 | 
				
			||||||
    if (process.env.NODE_ENV == 'development') {
 | 
					    if (process.env.NODE_ENV == 'development') {
 | 
				
			||||||
      Logger.log('Running Immich Server in DEVELOPMENT environment', 'ImmichServer');
 | 
					      Logger.log('Running Immich Server in DEVELOPMENT environment', 'ImmichServer');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@ import { AssetEntity } from '@app/database/entities/asset.entity';
 | 
				
			|||||||
import fs from 'fs';
 | 
					import fs from 'fs';
 | 
				
			||||||
import { SmartInfoEntity } from '@app/database/entities/smart-info.entity';
 | 
					import { SmartInfoEntity } from '@app/database/entities/smart-info.entity';
 | 
				
			||||||
import { Job } from 'bull';
 | 
					import { Job } from 'bull';
 | 
				
			||||||
 | 
					import { AssetResponseDto } from '../../api-v1/asset/response-dto/asset-response.dto';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Processor('background-task')
 | 
					@Processor('background-task')
 | 
				
			||||||
export class BackgroundTaskProcessor {
 | 
					export class BackgroundTaskProcessor {
 | 
				
			||||||
@@ -18,7 +19,7 @@ export class BackgroundTaskProcessor {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  // TODO: Should probably use constants / Interfaces for Queue names / data
 | 
					  // TODO: Should probably use constants / Interfaces for Queue names / data
 | 
				
			||||||
  @Process('delete-file-on-disk')
 | 
					  @Process('delete-file-on-disk')
 | 
				
			||||||
  async deleteFileOnDisk(job: Job<{ assets: AssetEntity[] }>) {
 | 
					  async deleteFileOnDisk(job: Job<{ assets: AssetResponseDto[] }>) {
 | 
				
			||||||
    const { assets } = job.data;
 | 
					    const { assets } = job.data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (const asset of assets) {
 | 
					    for (const asset of assets) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@ import { Injectable } from '@nestjs/common';
 | 
				
			|||||||
import { Queue } from 'bull';
 | 
					import { Queue } from 'bull';
 | 
				
			||||||
import { randomUUID } from 'node:crypto';
 | 
					import { randomUUID } from 'node:crypto';
 | 
				
			||||||
import { AssetEntity } from '@app/database/entities/asset.entity';
 | 
					import { AssetEntity } from '@app/database/entities/asset.entity';
 | 
				
			||||||
 | 
					import { AssetResponseDto } from '../../api-v1/asset/response-dto/asset-response.dto';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Injectable()
 | 
					@Injectable()
 | 
				
			||||||
export class BackgroundTaskService {
 | 
					export class BackgroundTaskService {
 | 
				
			||||||
@@ -11,7 +12,7 @@ export class BackgroundTaskService {
 | 
				
			|||||||
    private backgroundTaskQueue: Queue,
 | 
					    private backgroundTaskQueue: Queue,
 | 
				
			||||||
  ) {}
 | 
					  ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async deleteFileOnDisk(assets: AssetEntity[]) {
 | 
					  async deleteFileOnDisk(assets: AssetResponseDto[]) {
 | 
				
			||||||
    await this.backgroundTaskQueue.add(
 | 
					    await this.backgroundTaskQueue.add(
 | 
				
			||||||
      'delete-file-on-disk',
 | 
					      'delete-file-on-disk',
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -63,7 +63,7 @@ export class AssetUploadedProcessor {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Extract video duration if uploaded from the web & CLI
 | 
					    // Extract video duration if uploaded from the web & CLI
 | 
				
			||||||
    if (asset.type == AssetType.VIDEO && asset.duration == '0:00:00.000000') {
 | 
					    if (asset.type == AssetType.VIDEO) {
 | 
				
			||||||
      await this.metadataExtractionQueue.add(videoMetadataExtractionProcessorName, { asset }, { jobId: randomUUID() });
 | 
					      await this.metadataExtractionQueue.add(videoMetadataExtractionProcessorName, { asset }, { jobId: randomUUID() });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								server/immich-openapi-specs.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								server/immich-openapi-specs.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -5,7 +5,14 @@
 | 
				
			|||||||
  "root": "apps/immich",
 | 
					  "root": "apps/immich",
 | 
				
			||||||
  "compilerOptions": {
 | 
					  "compilerOptions": {
 | 
				
			||||||
    "webpack": false,
 | 
					    "webpack": false,
 | 
				
			||||||
    "tsConfigPath": "apps/immich/tsconfig.app.json"
 | 
					    "tsConfigPath": "apps/immich/tsconfig.app.json",
 | 
				
			||||||
 | 
					    "plugins": [  {
 | 
				
			||||||
 | 
					      "name": "@nestjs/swagger",
 | 
				
			||||||
 | 
					      "options": {
 | 
				
			||||||
 | 
					        "classValidatorShim": false,
 | 
				
			||||||
 | 
					        "introspectComments": true
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }]
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "projects": {
 | 
					  "projects": {
 | 
				
			||||||
    "immich": {
 | 
					    "immich": {
 | 
				
			||||||
@@ -45,4 +52,4 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										7
									
								
								server/openapitools.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								server/openapitools.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json",
 | 
				
			||||||
 | 
					  "spaces": 2,
 | 
				
			||||||
 | 
					  "generator-cli": {
 | 
				
			||||||
 | 
					    "version": "6.0.1"
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										594
									
								
								server/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										594
									
								
								server/package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -21,6 +21,7 @@
 | 
				
			|||||||
        "@nestjs/platform-fastify": "^8.4.7",
 | 
					        "@nestjs/platform-fastify": "^8.4.7",
 | 
				
			||||||
        "@nestjs/platform-socket.io": "^8.4.7",
 | 
					        "@nestjs/platform-socket.io": "^8.4.7",
 | 
				
			||||||
        "@nestjs/schedule": "^2.0.1",
 | 
					        "@nestjs/schedule": "^2.0.1",
 | 
				
			||||||
 | 
					        "@nestjs/swagger": "^5.2.1",
 | 
				
			||||||
        "@nestjs/typeorm": "^8.1.4",
 | 
					        "@nestjs/typeorm": "^8.1.4",
 | 
				
			||||||
        "@nestjs/websockets": "^8.4.7",
 | 
					        "@nestjs/websockets": "^8.4.7",
 | 
				
			||||||
        "@socket.io/redis-adapter": "^7.1.0",
 | 
					        "@socket.io/redis-adapter": "^7.1.0",
 | 
				
			||||||
@@ -44,6 +45,7 @@
 | 
				
			|||||||
        "rxjs": "^7.2.0",
 | 
					        "rxjs": "^7.2.0",
 | 
				
			||||||
        "sharp": "^0.28.0",
 | 
					        "sharp": "^0.28.0",
 | 
				
			||||||
        "socket.io-redis": "^6.1.1",
 | 
					        "socket.io-redis": "^6.1.1",
 | 
				
			||||||
 | 
					        "swagger-ui-express": "^4.4.0",
 | 
				
			||||||
        "systeminformation": "^5.11.0",
 | 
					        "systeminformation": "^5.11.0",
 | 
				
			||||||
        "typeorm": "^0.3.6"
 | 
					        "typeorm": "^0.3.6"
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
@@ -51,6 +53,7 @@
 | 
				
			|||||||
        "@nestjs/cli": "^8.2.8",
 | 
					        "@nestjs/cli": "^8.2.8",
 | 
				
			||||||
        "@nestjs/schematics": "^8.0.11",
 | 
					        "@nestjs/schematics": "^8.0.11",
 | 
				
			||||||
        "@nestjs/testing": "^8.4.7",
 | 
					        "@nestjs/testing": "^8.4.7",
 | 
				
			||||||
 | 
					        "@openapitools/openapi-generator-cli": "^2.5.1",
 | 
				
			||||||
        "@types/bcrypt": "^5.0.0",
 | 
					        "@types/bcrypt": "^5.0.0",
 | 
				
			||||||
        "@types/bull": "^3.15.7",
 | 
					        "@types/bull": "^3.15.7",
 | 
				
			||||||
        "@types/cron": "^2.0.0",
 | 
					        "@types/cron": "^2.0.0",
 | 
				
			||||||
@@ -1839,6 +1842,31 @@
 | 
				
			|||||||
      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
 | 
					      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
 | 
				
			||||||
      "dev": true
 | 
					      "dev": true
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/@nestjs/swagger": {
 | 
				
			||||||
 | 
					      "version": "5.2.1",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-5.2.1.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-7dNa08WCnTsW/oAk3Ujde+z64JMfNm19DhpXasFR8oJp/9pggYAbYU927HpA+GJsSFJX6adjIRZsCKUqaGWznw==",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "@nestjs/mapped-types": "1.0.1",
 | 
				
			||||||
 | 
					        "lodash": "4.17.21",
 | 
				
			||||||
 | 
					        "path-to-regexp": "3.2.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "peerDependencies": {
 | 
				
			||||||
 | 
					        "@nestjs/common": "^8.0.0",
 | 
				
			||||||
 | 
					        "@nestjs/core": "^8.0.0",
 | 
				
			||||||
 | 
					        "fastify-swagger": "*",
 | 
				
			||||||
 | 
					        "reflect-metadata": "^0.1.12",
 | 
				
			||||||
 | 
					        "swagger-ui-express": "*"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "peerDependenciesMeta": {
 | 
				
			||||||
 | 
					        "fastify-swagger": {
 | 
				
			||||||
 | 
					          "optional": true
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "swagger-ui-express": {
 | 
				
			||||||
 | 
					          "optional": true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/@nestjs/testing": {
 | 
					    "node_modules/@nestjs/testing": {
 | 
				
			||||||
      "version": "8.4.7",
 | 
					      "version": "8.4.7",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-8.4.7.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-8.4.7.tgz",
 | 
				
			||||||
@@ -1966,6 +1994,214 @@
 | 
				
			|||||||
        "npm": ">=5.0.0"
 | 
					        "npm": ">=5.0.0"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/@openapitools/openapi-generator-cli": {
 | 
				
			||||||
 | 
					      "version": "2.5.1",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/@openapitools/openapi-generator-cli/-/openapi-generator-cli-2.5.1.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-WSRQBU0dCSVD+0Qv8iCsv0C4iMaZe/NpJ/CT4SmrEYLH3txoKTE8wEfbdj/kqShS8Or0YEGDPUzhSIKY151L0w==",
 | 
				
			||||||
 | 
					      "dev": true,
 | 
				
			||||||
 | 
					      "hasInstallScript": true,
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "@nestjs/common": "8.4.4",
 | 
				
			||||||
 | 
					        "@nestjs/core": "8.4.4",
 | 
				
			||||||
 | 
					        "@nuxtjs/opencollective": "0.3.2",
 | 
				
			||||||
 | 
					        "chalk": "4.1.2",
 | 
				
			||||||
 | 
					        "commander": "8.3.0",
 | 
				
			||||||
 | 
					        "compare-versions": "4.1.3",
 | 
				
			||||||
 | 
					        "concurrently": "6.5.1",
 | 
				
			||||||
 | 
					        "console.table": "0.10.0",
 | 
				
			||||||
 | 
					        "fs-extra": "10.0.1",
 | 
				
			||||||
 | 
					        "glob": "7.1.6",
 | 
				
			||||||
 | 
					        "inquirer": "8.2.2",
 | 
				
			||||||
 | 
					        "lodash": "4.17.21",
 | 
				
			||||||
 | 
					        "reflect-metadata": "0.1.13",
 | 
				
			||||||
 | 
					        "rxjs": "7.5.5",
 | 
				
			||||||
 | 
					        "tslib": "2.0.3"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "bin": {
 | 
				
			||||||
 | 
					        "openapi-generator-cli": "main.js"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "node": ">=10.0.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "funding": {
 | 
				
			||||||
 | 
					        "type": "opencollective",
 | 
				
			||||||
 | 
					        "url": "https://opencollective.com/openapi_generator"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/@openapitools/openapi-generator-cli/node_modules/@nestjs/common": {
 | 
				
			||||||
 | 
					      "version": "8.4.4",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-8.4.4.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-QHi7QcgH/5Jinz+SCfIZJkFHc6Cch1YsAEGFEhi6wSp6MILb0sJMQ1CX06e9tCOAjSlBwaJj4PH0eFCVau5v9Q==",
 | 
				
			||||||
 | 
					      "dev": true,
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "axios": "0.26.1",
 | 
				
			||||||
 | 
					        "iterare": "1.2.1",
 | 
				
			||||||
 | 
					        "tslib": "2.3.1",
 | 
				
			||||||
 | 
					        "uuid": "8.3.2"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "funding": {
 | 
				
			||||||
 | 
					        "type": "opencollective",
 | 
				
			||||||
 | 
					        "url": "https://opencollective.com/nest"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "peerDependencies": {
 | 
				
			||||||
 | 
					        "cache-manager": "*",
 | 
				
			||||||
 | 
					        "class-transformer": "*",
 | 
				
			||||||
 | 
					        "class-validator": "*",
 | 
				
			||||||
 | 
					        "reflect-metadata": "^0.1.12",
 | 
				
			||||||
 | 
					        "rxjs": "^7.1.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "peerDependenciesMeta": {
 | 
				
			||||||
 | 
					        "cache-manager": {
 | 
				
			||||||
 | 
					          "optional": true
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "class-transformer": {
 | 
				
			||||||
 | 
					          "optional": true
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "class-validator": {
 | 
				
			||||||
 | 
					          "optional": true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/@openapitools/openapi-generator-cli/node_modules/@nestjs/common/node_modules/tslib": {
 | 
				
			||||||
 | 
					      "version": "2.3.1",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==",
 | 
				
			||||||
 | 
					      "dev": true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/@openapitools/openapi-generator-cli/node_modules/@nestjs/core": {
 | 
				
			||||||
 | 
					      "version": "8.4.4",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-8.4.4.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-Ef3yJPuzAttpNfehnGqIV5kHIL9SHptB5F4ERxoU7pT61H3xiYpZw6hSjx68cJO7cc6rm7/N+b4zeuJvFHtvBg==",
 | 
				
			||||||
 | 
					      "dev": true,
 | 
				
			||||||
 | 
					      "hasInstallScript": true,
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "@nuxtjs/opencollective": "0.3.2",
 | 
				
			||||||
 | 
					        "fast-safe-stringify": "2.1.1",
 | 
				
			||||||
 | 
					        "iterare": "1.2.1",
 | 
				
			||||||
 | 
					        "object-hash": "3.0.0",
 | 
				
			||||||
 | 
					        "path-to-regexp": "3.2.0",
 | 
				
			||||||
 | 
					        "tslib": "2.3.1",
 | 
				
			||||||
 | 
					        "uuid": "8.3.2"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "funding": {
 | 
				
			||||||
 | 
					        "type": "opencollective",
 | 
				
			||||||
 | 
					        "url": "https://opencollective.com/nest"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "peerDependencies": {
 | 
				
			||||||
 | 
					        "@nestjs/common": "^8.0.0",
 | 
				
			||||||
 | 
					        "@nestjs/microservices": "^8.0.0",
 | 
				
			||||||
 | 
					        "@nestjs/platform-express": "^8.0.0",
 | 
				
			||||||
 | 
					        "@nestjs/websockets": "^8.0.0",
 | 
				
			||||||
 | 
					        "reflect-metadata": "^0.1.12",
 | 
				
			||||||
 | 
					        "rxjs": "^7.1.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "peerDependenciesMeta": {
 | 
				
			||||||
 | 
					        "@nestjs/microservices": {
 | 
				
			||||||
 | 
					          "optional": true
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "@nestjs/platform-express": {
 | 
				
			||||||
 | 
					          "optional": true
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "@nestjs/websockets": {
 | 
				
			||||||
 | 
					          "optional": true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/@openapitools/openapi-generator-cli/node_modules/@nestjs/core/node_modules/tslib": {
 | 
				
			||||||
 | 
					      "version": "2.3.1",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==",
 | 
				
			||||||
 | 
					      "dev": true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/@openapitools/openapi-generator-cli/node_modules/commander": {
 | 
				
			||||||
 | 
					      "version": "8.3.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
 | 
				
			||||||
 | 
					      "dev": true,
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "node": ">= 12"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/@openapitools/openapi-generator-cli/node_modules/fs-extra": {
 | 
				
			||||||
 | 
					      "version": "10.0.1",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==",
 | 
				
			||||||
 | 
					      "dev": true,
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "graceful-fs": "^4.2.0",
 | 
				
			||||||
 | 
					        "jsonfile": "^6.0.1",
 | 
				
			||||||
 | 
					        "universalify": "^2.0.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "node": ">=12"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/@openapitools/openapi-generator-cli/node_modules/glob": {
 | 
				
			||||||
 | 
					      "version": "7.1.6",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
 | 
				
			||||||
 | 
					      "dev": true,
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "fs.realpath": "^1.0.0",
 | 
				
			||||||
 | 
					        "inflight": "^1.0.4",
 | 
				
			||||||
 | 
					        "inherits": "2",
 | 
				
			||||||
 | 
					        "minimatch": "^3.0.4",
 | 
				
			||||||
 | 
					        "once": "^1.3.0",
 | 
				
			||||||
 | 
					        "path-is-absolute": "^1.0.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "node": "*"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "funding": {
 | 
				
			||||||
 | 
					        "url": "https://github.com/sponsors/isaacs"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/@openapitools/openapi-generator-cli/node_modules/inquirer": {
 | 
				
			||||||
 | 
					      "version": "8.2.2",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.2.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-pG7I/si6K/0X7p1qU+rfWnpTE1UIkTONN1wxtzh0d+dHXtT/JG6qBgLxoyHVsQa8cFABxAPh0pD6uUUHiAoaow==",
 | 
				
			||||||
 | 
					      "dev": true,
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "ansi-escapes": "^4.2.1",
 | 
				
			||||||
 | 
					        "chalk": "^4.1.1",
 | 
				
			||||||
 | 
					        "cli-cursor": "^3.1.0",
 | 
				
			||||||
 | 
					        "cli-width": "^3.0.0",
 | 
				
			||||||
 | 
					        "external-editor": "^3.0.3",
 | 
				
			||||||
 | 
					        "figures": "^3.0.0",
 | 
				
			||||||
 | 
					        "lodash": "^4.17.21",
 | 
				
			||||||
 | 
					        "mute-stream": "0.0.8",
 | 
				
			||||||
 | 
					        "ora": "^5.4.1",
 | 
				
			||||||
 | 
					        "run-async": "^2.4.0",
 | 
				
			||||||
 | 
					        "rxjs": "^7.5.5",
 | 
				
			||||||
 | 
					        "string-width": "^4.1.0",
 | 
				
			||||||
 | 
					        "strip-ansi": "^6.0.0",
 | 
				
			||||||
 | 
					        "through": "^2.3.6"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "node": ">=12.0.0"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/@openapitools/openapi-generator-cli/node_modules/rxjs": {
 | 
				
			||||||
 | 
					      "version": "7.5.5",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==",
 | 
				
			||||||
 | 
					      "dev": true,
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "tslib": "^2.1.0"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/@openapitools/openapi-generator-cli/node_modules/rxjs/node_modules/tslib": {
 | 
				
			||||||
 | 
					      "version": "2.4.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
 | 
				
			||||||
 | 
					      "dev": true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/@openapitools/openapi-generator-cli/node_modules/tslib": {
 | 
				
			||||||
 | 
					      "version": "2.0.3",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==",
 | 
				
			||||||
 | 
					      "dev": true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/@sideway/address": {
 | 
					    "node_modules/@sideway/address": {
 | 
				
			||||||
      "version": "4.1.3",
 | 
					      "version": "4.1.3",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.3.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.3.tgz",
 | 
				
			||||||
@@ -3247,9 +3483,9 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/axios": {
 | 
					    "node_modules/axios": {
 | 
				
			||||||
      "version": "0.26.0",
 | 
					      "version": "0.26.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz",
 | 
				
			||||||
      "integrity": "sha512-lKoGLMYtHvFrPVt3r+RBMp9nh34N0M8zEfCWqdWZx6phynIEhQqAdydpyBAAG211zlhX9Rgu08cOamy6XjE5Og==",
 | 
					      "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
 | 
				
			||||||
      "dependencies": {
 | 
					      "dependencies": {
 | 
				
			||||||
        "follow-redirects": "^1.14.8"
 | 
					        "follow-redirects": "^1.14.8"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -3989,6 +4225,12 @@
 | 
				
			|||||||
      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
 | 
					      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
 | 
				
			||||||
      "dev": true
 | 
					      "dev": true
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/compare-versions": {
 | 
				
			||||||
 | 
					      "version": "4.1.3",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-4.1.3.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-WQfnbDcrYnGr55UwbxKiQKASnTtNnaAWVi8jZyy8NTpVAXWACSne8lMD1iaIo9AiU6mnuLvSVshCzewVuWxHUg==",
 | 
				
			||||||
 | 
					      "dev": true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/component-emitter": {
 | 
					    "node_modules/component-emitter": {
 | 
				
			||||||
      "version": "1.3.0",
 | 
					      "version": "1.3.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
 | 
				
			||||||
@@ -4040,6 +4282,61 @@
 | 
				
			|||||||
        "safe-buffer": "~5.1.0"
 | 
					        "safe-buffer": "~5.1.0"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/concurrently": {
 | 
				
			||||||
 | 
					      "version": "6.5.1",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-6.5.1.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-FlSwNpGjWQfRwPLXvJ/OgysbBxPkWpiVjy1042b0U7on7S7qwwMIILRj7WTN1mTgqa582bG6NFuScOoh6Zgdag==",
 | 
				
			||||||
 | 
					      "dev": true,
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "chalk": "^4.1.0",
 | 
				
			||||||
 | 
					        "date-fns": "^2.16.1",
 | 
				
			||||||
 | 
					        "lodash": "^4.17.21",
 | 
				
			||||||
 | 
					        "rxjs": "^6.6.3",
 | 
				
			||||||
 | 
					        "spawn-command": "^0.0.2-1",
 | 
				
			||||||
 | 
					        "supports-color": "^8.1.0",
 | 
				
			||||||
 | 
					        "tree-kill": "^1.2.2",
 | 
				
			||||||
 | 
					        "yargs": "^16.2.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "bin": {
 | 
				
			||||||
 | 
					        "concurrently": "bin/concurrently.js"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "node": ">=10.0.0"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/concurrently/node_modules/rxjs": {
 | 
				
			||||||
 | 
					      "version": "6.6.7",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
 | 
				
			||||||
 | 
					      "dev": true,
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "tslib": "^1.9.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "npm": ">=2.0.0"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/concurrently/node_modules/supports-color": {
 | 
				
			||||||
 | 
					      "version": "8.1.1",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
 | 
				
			||||||
 | 
					      "dev": true,
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "has-flag": "^4.0.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "node": ">=10"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "funding": {
 | 
				
			||||||
 | 
					        "url": "https://github.com/chalk/supports-color?sponsor=1"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/concurrently/node_modules/tslib": {
 | 
				
			||||||
 | 
					      "version": "1.14.1",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
 | 
				
			||||||
 | 
					      "dev": true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/consola": {
 | 
					    "node_modules/consola": {
 | 
				
			||||||
      "version": "2.15.3",
 | 
					      "version": "2.15.3",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz",
 | 
				
			||||||
@@ -4050,6 +4347,18 @@
 | 
				
			|||||||
      "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
 | 
				
			||||||
      "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
 | 
					      "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/console.table": {
 | 
				
			||||||
 | 
					      "version": "0.10.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/console.table/-/console.table-0.10.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-dPyZofqggxuvSf7WXvNjuRfnsOk1YazkVP8FdxH4tcH2c37wc79/Yl6Bhr7Lsu00KMgy2ql/qCMuNu8xctZM8g==",
 | 
				
			||||||
 | 
					      "dev": true,
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "easy-table": "1.1.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "node": "> 0.10"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/content-disposition": {
 | 
					    "node_modules/content-disposition": {
 | 
				
			||||||
      "version": "0.5.4",
 | 
					      "version": "0.5.4",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
 | 
				
			||||||
@@ -4521,6 +4830,15 @@
 | 
				
			|||||||
      "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
 | 
				
			||||||
      "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
 | 
					      "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/easy-table": {
 | 
				
			||||||
 | 
					      "version": "1.1.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.1.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-oq33hWOSSnl2Hoh00tZWaIPi1ievrD9aFG82/IgjlycAnW9hHx5PkJiXpxPsgEE+H7BsbVQXFVFST8TEXS6/pA==",
 | 
				
			||||||
 | 
					      "dev": true,
 | 
				
			||||||
 | 
					      "optionalDependencies": {
 | 
				
			||||||
 | 
					        "wcwidth": ">=1.0.1"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/ecdsa-sig-formatter": {
 | 
					    "node_modules/ecdsa-sig-formatter": {
 | 
				
			||||||
      "version": "1.0.11",
 | 
					      "version": "1.0.11",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
 | 
				
			||||||
@@ -9737,6 +10055,12 @@
 | 
				
			|||||||
      "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
 | 
					      "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
 | 
				
			||||||
      "dev": true
 | 
					      "dev": true
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/spawn-command": {
 | 
				
			||||||
 | 
					      "version": "0.0.2-1",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==",
 | 
				
			||||||
 | 
					      "dev": true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/spdx-correct": {
 | 
					    "node_modules/spdx-correct": {
 | 
				
			||||||
      "version": "3.1.1",
 | 
					      "version": "3.1.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
 | 
				
			||||||
@@ -9996,6 +10320,25 @@
 | 
				
			|||||||
        "url": "https://github.com/sponsors/ljharb"
 | 
					        "url": "https://github.com/sponsors/ljharb"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/swagger-ui-dist": {
 | 
				
			||||||
 | 
					      "version": "4.12.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.12.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-B0Iy2ueXtbByE6OOyHTi3lFQkpPi/L7kFOKFeKTr44za7dJIELa9kzaca6GkndCgpK1QTjArnoXG+aUy0XQp1w=="
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/swagger-ui-express": {
 | 
				
			||||||
 | 
					      "version": "4.4.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.4.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-1CzRkHG386VQMVZK406jcpgnW2a9A5A/NiAjKhsFTQqUBWRF+uGbXTU/mA7WSV3mTzyOQDvjBdWP/c2qd5lqKw==",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "swagger-ui-dist": ">=4.11.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "node": ">= v0.10.32"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "peerDependencies": {
 | 
				
			||||||
 | 
					        "express": ">=4.0.0"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/symbol-observable": {
 | 
					    "node_modules/symbol-observable": {
 | 
				
			||||||
      "version": "4.0.0",
 | 
					      "version": "4.0.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
 | 
				
			||||||
@@ -12510,6 +12853,16 @@
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "@nestjs/swagger": {
 | 
				
			||||||
 | 
					      "version": "5.2.1",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-5.2.1.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-7dNa08WCnTsW/oAk3Ujde+z64JMfNm19DhpXasFR8oJp/9pggYAbYU927HpA+GJsSFJX6adjIRZsCKUqaGWznw==",
 | 
				
			||||||
 | 
					      "requires": {
 | 
				
			||||||
 | 
					        "@nestjs/mapped-types": "1.0.1",
 | 
				
			||||||
 | 
					        "lodash": "4.17.21",
 | 
				
			||||||
 | 
					        "path-to-regexp": "3.2.0"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "@nestjs/testing": {
 | 
					    "@nestjs/testing": {
 | 
				
			||||||
      "version": "8.4.7",
 | 
					      "version": "8.4.7",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-8.4.7.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-8.4.7.tgz",
 | 
				
			||||||
@@ -12588,6 +12941,150 @@
 | 
				
			|||||||
        "node-fetch": "^2.6.1"
 | 
					        "node-fetch": "^2.6.1"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "@openapitools/openapi-generator-cli": {
 | 
				
			||||||
 | 
					      "version": "2.5.1",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/@openapitools/openapi-generator-cli/-/openapi-generator-cli-2.5.1.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-WSRQBU0dCSVD+0Qv8iCsv0C4iMaZe/NpJ/CT4SmrEYLH3txoKTE8wEfbdj/kqShS8Or0YEGDPUzhSIKY151L0w==",
 | 
				
			||||||
 | 
					      "dev": true,
 | 
				
			||||||
 | 
					      "requires": {
 | 
				
			||||||
 | 
					        "@nestjs/common": "8.4.4",
 | 
				
			||||||
 | 
					        "@nestjs/core": "8.4.4",
 | 
				
			||||||
 | 
					        "@nuxtjs/opencollective": "0.3.2",
 | 
				
			||||||
 | 
					        "chalk": "4.1.2",
 | 
				
			||||||
 | 
					        "commander": "8.3.0",
 | 
				
			||||||
 | 
					        "compare-versions": "4.1.3",
 | 
				
			||||||
 | 
					        "concurrently": "6.5.1",
 | 
				
			||||||
 | 
					        "console.table": "0.10.0",
 | 
				
			||||||
 | 
					        "fs-extra": "10.0.1",
 | 
				
			||||||
 | 
					        "glob": "7.1.6",
 | 
				
			||||||
 | 
					        "inquirer": "8.2.2",
 | 
				
			||||||
 | 
					        "lodash": "4.17.21",
 | 
				
			||||||
 | 
					        "reflect-metadata": "0.1.13",
 | 
				
			||||||
 | 
					        "rxjs": "7.5.5",
 | 
				
			||||||
 | 
					        "tslib": "2.0.3"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "@nestjs/common": {
 | 
				
			||||||
 | 
					          "version": "8.4.4",
 | 
				
			||||||
 | 
					          "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-8.4.4.tgz",
 | 
				
			||||||
 | 
					          "integrity": "sha512-QHi7QcgH/5Jinz+SCfIZJkFHc6Cch1YsAEGFEhi6wSp6MILb0sJMQ1CX06e9tCOAjSlBwaJj4PH0eFCVau5v9Q==",
 | 
				
			||||||
 | 
					          "dev": true,
 | 
				
			||||||
 | 
					          "requires": {
 | 
				
			||||||
 | 
					            "axios": "0.26.1",
 | 
				
			||||||
 | 
					            "iterare": "1.2.1",
 | 
				
			||||||
 | 
					            "tslib": "2.3.1",
 | 
				
			||||||
 | 
					            "uuid": "8.3.2"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "dependencies": {
 | 
				
			||||||
 | 
					            "tslib": {
 | 
				
			||||||
 | 
					              "version": "2.3.1",
 | 
				
			||||||
 | 
					              "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
 | 
				
			||||||
 | 
					              "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==",
 | 
				
			||||||
 | 
					              "dev": true
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "@nestjs/core": {
 | 
				
			||||||
 | 
					          "version": "8.4.4",
 | 
				
			||||||
 | 
					          "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-8.4.4.tgz",
 | 
				
			||||||
 | 
					          "integrity": "sha512-Ef3yJPuzAttpNfehnGqIV5kHIL9SHptB5F4ERxoU7pT61H3xiYpZw6hSjx68cJO7cc6rm7/N+b4zeuJvFHtvBg==",
 | 
				
			||||||
 | 
					          "dev": true,
 | 
				
			||||||
 | 
					          "requires": {
 | 
				
			||||||
 | 
					            "@nuxtjs/opencollective": "0.3.2",
 | 
				
			||||||
 | 
					            "fast-safe-stringify": "2.1.1",
 | 
				
			||||||
 | 
					            "iterare": "1.2.1",
 | 
				
			||||||
 | 
					            "object-hash": "3.0.0",
 | 
				
			||||||
 | 
					            "path-to-regexp": "3.2.0",
 | 
				
			||||||
 | 
					            "tslib": "2.3.1",
 | 
				
			||||||
 | 
					            "uuid": "8.3.2"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "dependencies": {
 | 
				
			||||||
 | 
					            "tslib": {
 | 
				
			||||||
 | 
					              "version": "2.3.1",
 | 
				
			||||||
 | 
					              "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
 | 
				
			||||||
 | 
					              "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==",
 | 
				
			||||||
 | 
					              "dev": true
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "commander": {
 | 
				
			||||||
 | 
					          "version": "8.3.0",
 | 
				
			||||||
 | 
					          "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
 | 
				
			||||||
 | 
					          "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
 | 
				
			||||||
 | 
					          "dev": true
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "fs-extra": {
 | 
				
			||||||
 | 
					          "version": "10.0.1",
 | 
				
			||||||
 | 
					          "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz",
 | 
				
			||||||
 | 
					          "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==",
 | 
				
			||||||
 | 
					          "dev": true,
 | 
				
			||||||
 | 
					          "requires": {
 | 
				
			||||||
 | 
					            "graceful-fs": "^4.2.0",
 | 
				
			||||||
 | 
					            "jsonfile": "^6.0.1",
 | 
				
			||||||
 | 
					            "universalify": "^2.0.0"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "glob": {
 | 
				
			||||||
 | 
					          "version": "7.1.6",
 | 
				
			||||||
 | 
					          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
 | 
				
			||||||
 | 
					          "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
 | 
				
			||||||
 | 
					          "dev": true,
 | 
				
			||||||
 | 
					          "requires": {
 | 
				
			||||||
 | 
					            "fs.realpath": "^1.0.0",
 | 
				
			||||||
 | 
					            "inflight": "^1.0.4",
 | 
				
			||||||
 | 
					            "inherits": "2",
 | 
				
			||||||
 | 
					            "minimatch": "^3.0.4",
 | 
				
			||||||
 | 
					            "once": "^1.3.0",
 | 
				
			||||||
 | 
					            "path-is-absolute": "^1.0.0"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "inquirer": {
 | 
				
			||||||
 | 
					          "version": "8.2.2",
 | 
				
			||||||
 | 
					          "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.2.tgz",
 | 
				
			||||||
 | 
					          "integrity": "sha512-pG7I/si6K/0X7p1qU+rfWnpTE1UIkTONN1wxtzh0d+dHXtT/JG6qBgLxoyHVsQa8cFABxAPh0pD6uUUHiAoaow==",
 | 
				
			||||||
 | 
					          "dev": true,
 | 
				
			||||||
 | 
					          "requires": {
 | 
				
			||||||
 | 
					            "ansi-escapes": "^4.2.1",
 | 
				
			||||||
 | 
					            "chalk": "^4.1.1",
 | 
				
			||||||
 | 
					            "cli-cursor": "^3.1.0",
 | 
				
			||||||
 | 
					            "cli-width": "^3.0.0",
 | 
				
			||||||
 | 
					            "external-editor": "^3.0.3",
 | 
				
			||||||
 | 
					            "figures": "^3.0.0",
 | 
				
			||||||
 | 
					            "lodash": "^4.17.21",
 | 
				
			||||||
 | 
					            "mute-stream": "0.0.8",
 | 
				
			||||||
 | 
					            "ora": "^5.4.1",
 | 
				
			||||||
 | 
					            "run-async": "^2.4.0",
 | 
				
			||||||
 | 
					            "rxjs": "^7.5.5",
 | 
				
			||||||
 | 
					            "string-width": "^4.1.0",
 | 
				
			||||||
 | 
					            "strip-ansi": "^6.0.0",
 | 
				
			||||||
 | 
					            "through": "^2.3.6"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "rxjs": {
 | 
				
			||||||
 | 
					          "version": "7.5.5",
 | 
				
			||||||
 | 
					          "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz",
 | 
				
			||||||
 | 
					          "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==",
 | 
				
			||||||
 | 
					          "dev": true,
 | 
				
			||||||
 | 
					          "requires": {
 | 
				
			||||||
 | 
					            "tslib": "^2.1.0"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "dependencies": {
 | 
				
			||||||
 | 
					            "tslib": {
 | 
				
			||||||
 | 
					              "version": "2.4.0",
 | 
				
			||||||
 | 
					              "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
 | 
				
			||||||
 | 
					              "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
 | 
				
			||||||
 | 
					              "dev": true
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "tslib": {
 | 
				
			||||||
 | 
					          "version": "2.0.3",
 | 
				
			||||||
 | 
					          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
 | 
				
			||||||
 | 
					          "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==",
 | 
				
			||||||
 | 
					          "dev": true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "@sideway/address": {
 | 
					    "@sideway/address": {
 | 
				
			||||||
      "version": "4.1.3",
 | 
					      "version": "4.1.3",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.3.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.3.tgz",
 | 
				
			||||||
@@ -13697,9 +14194,9 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "axios": {
 | 
					    "axios": {
 | 
				
			||||||
      "version": "0.26.0",
 | 
					      "version": "0.26.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz",
 | 
				
			||||||
      "integrity": "sha512-lKoGLMYtHvFrPVt3r+RBMp9nh34N0M8zEfCWqdWZx6phynIEhQqAdydpyBAAG211zlhX9Rgu08cOamy6XjE5Og==",
 | 
					      "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
 | 
				
			||||||
      "requires": {
 | 
					      "requires": {
 | 
				
			||||||
        "follow-redirects": "^1.14.8"
 | 
					        "follow-redirects": "^1.14.8"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -14259,6 +14756,12 @@
 | 
				
			|||||||
      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
 | 
					      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
 | 
				
			||||||
      "dev": true
 | 
					      "dev": true
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "compare-versions": {
 | 
				
			||||||
 | 
					      "version": "4.1.3",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-4.1.3.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-WQfnbDcrYnGr55UwbxKiQKASnTtNnaAWVi8jZyy8NTpVAXWACSne8lMD1iaIo9AiU6mnuLvSVshCzewVuWxHUg==",
 | 
				
			||||||
 | 
					      "dev": true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "component-emitter": {
 | 
					    "component-emitter": {
 | 
				
			||||||
      "version": "1.3.0",
 | 
					      "version": "1.3.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
 | 
				
			||||||
@@ -14309,6 +14812,48 @@
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "concurrently": {
 | 
				
			||||||
 | 
					      "version": "6.5.1",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-6.5.1.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-FlSwNpGjWQfRwPLXvJ/OgysbBxPkWpiVjy1042b0U7on7S7qwwMIILRj7WTN1mTgqa582bG6NFuScOoh6Zgdag==",
 | 
				
			||||||
 | 
					      "dev": true,
 | 
				
			||||||
 | 
					      "requires": {
 | 
				
			||||||
 | 
					        "chalk": "^4.1.0",
 | 
				
			||||||
 | 
					        "date-fns": "^2.16.1",
 | 
				
			||||||
 | 
					        "lodash": "^4.17.21",
 | 
				
			||||||
 | 
					        "rxjs": "^6.6.3",
 | 
				
			||||||
 | 
					        "spawn-command": "^0.0.2-1",
 | 
				
			||||||
 | 
					        "supports-color": "^8.1.0",
 | 
				
			||||||
 | 
					        "tree-kill": "^1.2.2",
 | 
				
			||||||
 | 
					        "yargs": "^16.2.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "rxjs": {
 | 
				
			||||||
 | 
					          "version": "6.6.7",
 | 
				
			||||||
 | 
					          "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
 | 
				
			||||||
 | 
					          "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
 | 
				
			||||||
 | 
					          "dev": true,
 | 
				
			||||||
 | 
					          "requires": {
 | 
				
			||||||
 | 
					            "tslib": "^1.9.0"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "supports-color": {
 | 
				
			||||||
 | 
					          "version": "8.1.1",
 | 
				
			||||||
 | 
					          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
 | 
				
			||||||
 | 
					          "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
 | 
				
			||||||
 | 
					          "dev": true,
 | 
				
			||||||
 | 
					          "requires": {
 | 
				
			||||||
 | 
					            "has-flag": "^4.0.0"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "tslib": {
 | 
				
			||||||
 | 
					          "version": "1.14.1",
 | 
				
			||||||
 | 
					          "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
 | 
				
			||||||
 | 
					          "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
 | 
				
			||||||
 | 
					          "dev": true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "consola": {
 | 
					    "consola": {
 | 
				
			||||||
      "version": "2.15.3",
 | 
					      "version": "2.15.3",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz",
 | 
				
			||||||
@@ -14319,6 +14864,15 @@
 | 
				
			|||||||
      "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
 | 
				
			||||||
      "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
 | 
					      "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "console.table": {
 | 
				
			||||||
 | 
					      "version": "0.10.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/console.table/-/console.table-0.10.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-dPyZofqggxuvSf7WXvNjuRfnsOk1YazkVP8FdxH4tcH2c37wc79/Yl6Bhr7Lsu00KMgy2ql/qCMuNu8xctZM8g==",
 | 
				
			||||||
 | 
					      "dev": true,
 | 
				
			||||||
 | 
					      "requires": {
 | 
				
			||||||
 | 
					        "easy-table": "1.1.0"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "content-disposition": {
 | 
					    "content-disposition": {
 | 
				
			||||||
      "version": "0.5.4",
 | 
					      "version": "0.5.4",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
 | 
				
			||||||
@@ -14683,6 +15237,15 @@
 | 
				
			|||||||
      "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
 | 
				
			||||||
      "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
 | 
					      "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "easy-table": {
 | 
				
			||||||
 | 
					      "version": "1.1.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.1.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-oq33hWOSSnl2Hoh00tZWaIPi1ievrD9aFG82/IgjlycAnW9hHx5PkJiXpxPsgEE+H7BsbVQXFVFST8TEXS6/pA==",
 | 
				
			||||||
 | 
					      "dev": true,
 | 
				
			||||||
 | 
					      "requires": {
 | 
				
			||||||
 | 
					        "wcwidth": ">=1.0.1"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "ecdsa-sig-formatter": {
 | 
					    "ecdsa-sig-formatter": {
 | 
				
			||||||
      "version": "1.0.11",
 | 
					      "version": "1.0.11",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
 | 
				
			||||||
@@ -18655,6 +19218,12 @@
 | 
				
			|||||||
      "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
 | 
					      "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
 | 
				
			||||||
      "dev": true
 | 
					      "dev": true
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "spawn-command": {
 | 
				
			||||||
 | 
					      "version": "0.0.2-1",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==",
 | 
				
			||||||
 | 
					      "dev": true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "spdx-correct": {
 | 
					    "spdx-correct": {
 | 
				
			||||||
      "version": "3.1.1",
 | 
					      "version": "3.1.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
 | 
				
			||||||
@@ -18858,6 +19427,19 @@
 | 
				
			|||||||
      "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
 | 
				
			||||||
      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
 | 
					      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "swagger-ui-dist": {
 | 
				
			||||||
 | 
					      "version": "4.12.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.12.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-B0Iy2ueXtbByE6OOyHTi3lFQkpPi/L7kFOKFeKTr44za7dJIELa9kzaca6GkndCgpK1QTjArnoXG+aUy0XQp1w=="
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "swagger-ui-express": {
 | 
				
			||||||
 | 
					      "version": "4.4.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.4.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-1CzRkHG386VQMVZK406jcpgnW2a9A5A/NiAjKhsFTQqUBWRF+uGbXTU/mA7WSV3mTzyOQDvjBdWP/c2qd5lqKw==",
 | 
				
			||||||
 | 
					      "requires": {
 | 
				
			||||||
 | 
					        "swagger-ui-dist": ">=4.11.0"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "symbol-observable": {
 | 
					    "symbol-observable": {
 | 
				
			||||||
      "version": "4.0.0",
 | 
					      "version": "4.0.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,7 +22,8 @@
 | 
				
			|||||||
    "test:cov": "jest --coverage",
 | 
					    "test:cov": "jest --coverage",
 | 
				
			||||||
    "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
 | 
					    "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
 | 
				
			||||||
    "test:e2e": "jest --config ./apps/immich/test/jest-e2e.json",
 | 
					    "test:e2e": "jest --config ./apps/immich/test/jest-e2e.json",
 | 
				
			||||||
    "typeorm": "node --require ts-node/register ./node_modules/typeorm/cli.js"
 | 
					    "typeorm": "node --require ts-node/register ./node_modules/typeorm/cli.js",
 | 
				
			||||||
 | 
					    "api:generate-typescript": "rm -rf ../web/src/lib/open-api && npx openapi-generator-cli generate -g typescript-axios -i ./immich-openapi-specs.json -o ../web/src/lib/open-api"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "@mapbox/mapbox-sdk": "^0.13.3",
 | 
					    "@mapbox/mapbox-sdk": "^0.13.3",
 | 
				
			||||||
@@ -37,6 +38,7 @@
 | 
				
			|||||||
    "@nestjs/platform-fastify": "^8.4.7",
 | 
					    "@nestjs/platform-fastify": "^8.4.7",
 | 
				
			||||||
    "@nestjs/platform-socket.io": "^8.4.7",
 | 
					    "@nestjs/platform-socket.io": "^8.4.7",
 | 
				
			||||||
    "@nestjs/schedule": "^2.0.1",
 | 
					    "@nestjs/schedule": "^2.0.1",
 | 
				
			||||||
 | 
					    "@nestjs/swagger": "^5.2.1",
 | 
				
			||||||
    "@nestjs/typeorm": "^8.1.4",
 | 
					    "@nestjs/typeorm": "^8.1.4",
 | 
				
			||||||
    "@nestjs/websockets": "^8.4.7",
 | 
					    "@nestjs/websockets": "^8.4.7",
 | 
				
			||||||
    "@socket.io/redis-adapter": "^7.1.0",
 | 
					    "@socket.io/redis-adapter": "^7.1.0",
 | 
				
			||||||
@@ -60,6 +62,7 @@
 | 
				
			|||||||
    "rxjs": "^7.2.0",
 | 
					    "rxjs": "^7.2.0",
 | 
				
			||||||
    "sharp": "^0.28.0",
 | 
					    "sharp": "^0.28.0",
 | 
				
			||||||
    "socket.io-redis": "^6.1.1",
 | 
					    "socket.io-redis": "^6.1.1",
 | 
				
			||||||
 | 
					    "swagger-ui-express": "^4.4.0",
 | 
				
			||||||
    "systeminformation": "^5.11.0",
 | 
					    "systeminformation": "^5.11.0",
 | 
				
			||||||
    "typeorm": "^0.3.6"
 | 
					    "typeorm": "^0.3.6"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
@@ -83,6 +86,7 @@
 | 
				
			|||||||
    "@types/supertest": "^2.0.11",
 | 
					    "@types/supertest": "^2.0.11",
 | 
				
			||||||
    "@typescript-eslint/eslint-plugin": "^5.0.0",
 | 
					    "@typescript-eslint/eslint-plugin": "^5.0.0",
 | 
				
			||||||
    "@typescript-eslint/parser": "^5.0.0",
 | 
					    "@typescript-eslint/parser": "^5.0.0",
 | 
				
			||||||
 | 
					    "@openapitools/openapi-generator-cli": "2.5.1",
 | 
				
			||||||
    "eslint": "^8.0.1",
 | 
					    "eslint": "^8.0.1",
 | 
				
			||||||
    "eslint-config-prettier": "^8.3.0",
 | 
					    "eslint-config-prettier": "^8.3.0",
 | 
				
			||||||
    "eslint-plugin-prettier": "^4.0.0",
 | 
					    "eslint-plugin-prettier": "^4.0.0",
 | 
				
			||||||
@@ -124,4 +128,4 @@
 | 
				
			|||||||
      "^@app/job(|/.*)$": "<rootDir>/libs/job/src/$1"
 | 
					      "^@app/job(|/.*)$": "<rootDir>/libs/job/src/$1"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,15 +6,15 @@ module.exports = {
 | 
				
			|||||||
	ignorePatterns: ['*.cjs'],
 | 
						ignorePatterns: ['*.cjs'],
 | 
				
			||||||
	overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
 | 
						overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
 | 
				
			||||||
	settings: {
 | 
						settings: {
 | 
				
			||||||
		'svelte3/typescript': () => require('typescript')
 | 
							'svelte3/typescript': () => require('typescript'),
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	parserOptions: {
 | 
						parserOptions: {
 | 
				
			||||||
		sourceType: 'module',
 | 
							sourceType: 'module',
 | 
				
			||||||
		ecmaVersion: 2020
 | 
							ecmaVersion: 2020,
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	env: {
 | 
						env: {
 | 
				
			||||||
		browser: true,
 | 
							browser: true,
 | 
				
			||||||
		es2017: true,
 | 
							es2017: true,
 | 
				
			||||||
		node: true
 | 
							node: true,
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										34
									
								
								web/src/lib/immich-api/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								web/src/lib/immich-api/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					import {
 | 
				
			||||||
 | 
						AlbumApi,
 | 
				
			||||||
 | 
						AssetApi,
 | 
				
			||||||
 | 
						AuthenticationApi,
 | 
				
			||||||
 | 
						Configuration,
 | 
				
			||||||
 | 
						DeviceInfoApi,
 | 
				
			||||||
 | 
						ServerInfoApi,
 | 
				
			||||||
 | 
						UserApi,
 | 
				
			||||||
 | 
					} from '../open-api';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ImmichApi {
 | 
				
			||||||
 | 
						public userApi: UserApi;
 | 
				
			||||||
 | 
						public albumApi: AlbumApi;
 | 
				
			||||||
 | 
						public assetApi: AssetApi;
 | 
				
			||||||
 | 
						public authenticationApi: AuthenticationApi;
 | 
				
			||||||
 | 
						public deviceInfoApi: DeviceInfoApi;
 | 
				
			||||||
 | 
						public serverInfoApi: ServerInfoApi;
 | 
				
			||||||
 | 
						private config = new Configuration();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						constructor() {
 | 
				
			||||||
 | 
							this.userApi = new UserApi(this.config);
 | 
				
			||||||
 | 
							this.albumApi = new AlbumApi(this.config);
 | 
				
			||||||
 | 
							this.assetApi = new AssetApi(this.config);
 | 
				
			||||||
 | 
							this.authenticationApi = new AuthenticationApi(this.config);
 | 
				
			||||||
 | 
							this.deviceInfoApi = new DeviceInfoApi(this.config);
 | 
				
			||||||
 | 
							this.serverInfoApi = new ServerInfoApi(this.config);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public setAccessToken(accessToken: string) {
 | 
				
			||||||
 | 
							this.config.accessToken = accessToken;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const immichApi = new ImmichApi();
 | 
				
			||||||
							
								
								
									
										4
									
								
								web/src/lib/open-api/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								web/src/lib/open-api/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					wwwroot/*.js
 | 
				
			||||||
 | 
					node_modules
 | 
				
			||||||
 | 
					typings
 | 
				
			||||||
 | 
					dist
 | 
				
			||||||
							
								
								
									
										1
									
								
								web/src/lib/open-api/.npmignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								web/src/lib/open-api/.npmignore
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					# empty npmignore to ensure all required files (e.g., in the dist folder) are published by npm
 | 
				
			||||||
							
								
								
									
										23
									
								
								web/src/lib/open-api/.openapi-generator-ignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								web/src/lib/open-api/.openapi-generator-ignore
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					# OpenAPI Generator Ignore
 | 
				
			||||||
 | 
					# Generated by openapi-generator https://github.com/openapitools/openapi-generator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Use this file to prevent files from being overwritten by the generator.
 | 
				
			||||||
 | 
					# The patterns follow closely to .gitignore or .dockerignore.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# As an example, the C# client generator defines ApiClient.cs.
 | 
				
			||||||
 | 
					# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
 | 
				
			||||||
 | 
					#ApiClient.cs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# You can match any string of characters against a directory, file or extension with a single asterisk (*):
 | 
				
			||||||
 | 
					#foo/*/qux
 | 
				
			||||||
 | 
					# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
 | 
				
			||||||
 | 
					#foo/**/qux
 | 
				
			||||||
 | 
					# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# You can also negate patterns with an exclamation (!).
 | 
				
			||||||
 | 
					# For example, you can ignore all files in a docs folder with the file extension .md:
 | 
				
			||||||
 | 
					#docs/*.md
 | 
				
			||||||
 | 
					# Then explicitly reverse the ignore rule for a single file:
 | 
				
			||||||
 | 
					#!docs/README.md
 | 
				
			||||||
							
								
								
									
										9
									
								
								web/src/lib/open-api/.openapi-generator/FILES
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								web/src/lib/open-api/.openapi-generator/FILES
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					.gitignore
 | 
				
			||||||
 | 
					.npmignore
 | 
				
			||||||
 | 
					.openapi-generator-ignore
 | 
				
			||||||
 | 
					api.ts
 | 
				
			||||||
 | 
					base.ts
 | 
				
			||||||
 | 
					common.ts
 | 
				
			||||||
 | 
					configuration.ts
 | 
				
			||||||
 | 
					git_push.sh
 | 
				
			||||||
 | 
					index.ts
 | 
				
			||||||
							
								
								
									
										1
									
								
								web/src/lib/open-api/.openapi-generator/VERSION
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								web/src/lib/open-api/.openapi-generator/VERSION
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					6.0.1
 | 
				
			||||||
							
								
								
									
										3874
									
								
								web/src/lib/open-api/api.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3874
									
								
								web/src/lib/open-api/api.ts
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										74
									
								
								web/src/lib/open-api/base.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								web/src/lib/open-api/base.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,74 @@
 | 
				
			|||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Immich
 | 
				
			||||||
 | 
					 * Immich API
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The version of the OpenAPI document: 1.17.0
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 | 
				
			||||||
 | 
					 * https://openapi-generator.tech
 | 
				
			||||||
 | 
					 * Do not edit the class manually.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { Configuration } from './configuration';
 | 
				
			||||||
 | 
					// Some imports not used depending on template conditions
 | 
				
			||||||
 | 
					// @ts-ignore
 | 
				
			||||||
 | 
					import globalAxios, { AxiosPromise, AxiosInstance, AxiosRequestConfig } from 'axios';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const BASE_PATH = '/api'.replace(/\/+$/, '');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @export
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export const COLLECTION_FORMATS = {
 | 
				
			||||||
 | 
						csv: ',',
 | 
				
			||||||
 | 
						ssv: ' ',
 | 
				
			||||||
 | 
						tsv: '\t',
 | 
				
			||||||
 | 
						pipes: '|',
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @export
 | 
				
			||||||
 | 
					 * @interface RequestArgs
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export interface RequestArgs {
 | 
				
			||||||
 | 
						url: string;
 | 
				
			||||||
 | 
						options: AxiosRequestConfig;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @export
 | 
				
			||||||
 | 
					 * @class BaseAPI
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export class BaseAPI {
 | 
				
			||||||
 | 
						protected configuration: Configuration | undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						constructor(
 | 
				
			||||||
 | 
							configuration?: Configuration,
 | 
				
			||||||
 | 
							protected basePath: string = BASE_PATH,
 | 
				
			||||||
 | 
							protected axios: AxiosInstance = globalAxios,
 | 
				
			||||||
 | 
						) {
 | 
				
			||||||
 | 
							if (configuration) {
 | 
				
			||||||
 | 
								this.configuration = configuration;
 | 
				
			||||||
 | 
								this.basePath = configuration.basePath || this.basePath;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @export
 | 
				
			||||||
 | 
					 * @class RequiredError
 | 
				
			||||||
 | 
					 * @extends {Error}
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export class RequiredError extends Error {
 | 
				
			||||||
 | 
						name: 'RequiredError' = 'RequiredError';
 | 
				
			||||||
 | 
						constructor(public field: string, msg?: string) {
 | 
				
			||||||
 | 
							super(msg);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										138
									
								
								web/src/lib/open-api/common.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								web/src/lib/open-api/common.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,138 @@
 | 
				
			|||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Immich
 | 
				
			||||||
 | 
					 * Immich API
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The version of the OpenAPI document: 1.17.0
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 | 
				
			||||||
 | 
					 * https://openapi-generator.tech
 | 
				
			||||||
 | 
					 * Do not edit the class manually.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { Configuration } from "./configuration";
 | 
				
			||||||
 | 
					import { RequiredError, RequestArgs } from "./base";
 | 
				
			||||||
 | 
					import { AxiosInstance, AxiosResponse } from 'axios';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @export
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export const DUMMY_BASE_URL = 'https://example.com'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @throws {RequiredError}
 | 
				
			||||||
 | 
					 * @export
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export const assertParamExists = function (functionName: string, paramName: string, paramValue: unknown) {
 | 
				
			||||||
 | 
					    if (paramValue === null || paramValue === undefined) {
 | 
				
			||||||
 | 
					        throw new RequiredError(paramName, `Required parameter ${paramName} was null or undefined when calling ${functionName}.`);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @export
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export const setApiKeyToObject = async function (object: any, keyParamName: string, configuration?: Configuration) {
 | 
				
			||||||
 | 
					    if (configuration && configuration.apiKey) {
 | 
				
			||||||
 | 
					        const localVarApiKeyValue = typeof configuration.apiKey === 'function'
 | 
				
			||||||
 | 
					            ? await configuration.apiKey(keyParamName)
 | 
				
			||||||
 | 
					            : await configuration.apiKey;
 | 
				
			||||||
 | 
					        object[keyParamName] = localVarApiKeyValue;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @export
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export const setBasicAuthToObject = function (object: any, configuration?: Configuration) {
 | 
				
			||||||
 | 
					    if (configuration && (configuration.username || configuration.password)) {
 | 
				
			||||||
 | 
					        object["auth"] = { username: configuration.username, password: configuration.password };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @export
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export const setBearerAuthToObject = async function (object: any, configuration?: Configuration) {
 | 
				
			||||||
 | 
					    if (configuration && configuration.accessToken) {
 | 
				
			||||||
 | 
					        const accessToken = typeof configuration.accessToken === 'function'
 | 
				
			||||||
 | 
					            ? await configuration.accessToken()
 | 
				
			||||||
 | 
					            : await configuration.accessToken;
 | 
				
			||||||
 | 
					        object["Authorization"] = "Bearer " + accessToken;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @export
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export const setOAuthToObject = async function (object: any, name: string, scopes: string[], configuration?: Configuration) {
 | 
				
			||||||
 | 
					    if (configuration && configuration.accessToken) {
 | 
				
			||||||
 | 
					        const localVarAccessTokenValue = typeof configuration.accessToken === 'function'
 | 
				
			||||||
 | 
					            ? await configuration.accessToken(name, scopes)
 | 
				
			||||||
 | 
					            : await configuration.accessToken;
 | 
				
			||||||
 | 
					        object["Authorization"] = "Bearer " + localVarAccessTokenValue;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @export
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export const setSearchParams = function (url: URL, ...objects: any[]) {
 | 
				
			||||||
 | 
					    const searchParams = new URLSearchParams(url.search);
 | 
				
			||||||
 | 
					    for (const object of objects) {
 | 
				
			||||||
 | 
					        for (const key in object) {
 | 
				
			||||||
 | 
					            if (Array.isArray(object[key])) {
 | 
				
			||||||
 | 
					                searchParams.delete(key);
 | 
				
			||||||
 | 
					                for (const item of object[key]) {
 | 
				
			||||||
 | 
					                    searchParams.append(key, item);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                searchParams.set(key, object[key]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    url.search = searchParams.toString();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @export
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export const serializeDataIfNeeded = function (value: any, requestOptions: any, configuration?: Configuration) {
 | 
				
			||||||
 | 
					    const nonString = typeof value !== 'string';
 | 
				
			||||||
 | 
					    const needsSerialization = nonString && configuration && configuration.isJsonMime
 | 
				
			||||||
 | 
					        ? configuration.isJsonMime(requestOptions.headers['Content-Type'])
 | 
				
			||||||
 | 
					        : nonString;
 | 
				
			||||||
 | 
					    return needsSerialization
 | 
				
			||||||
 | 
					        ? JSON.stringify(value !== undefined ? value : {})
 | 
				
			||||||
 | 
					        : (value || "");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @export
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export const toPathString = function (url: URL) {
 | 
				
			||||||
 | 
					    return url.pathname + url.search + url.hash
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @export
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export const createRequestFunction = function (axiosArgs: RequestArgs, globalAxios: AxiosInstance, BASE_PATH: string, configuration?: Configuration) {
 | 
				
			||||||
 | 
					    return <T = unknown, R = AxiosResponse<T>>(axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
 | 
				
			||||||
 | 
					        const axiosRequestArgs = {...axiosArgs.options, url: (configuration?.basePath || basePath) + axiosArgs.url};
 | 
				
			||||||
 | 
					        return axios.request<T, R>(axiosRequestArgs);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										101
									
								
								web/src/lib/open-api/configuration.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								web/src/lib/open-api/configuration.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,101 @@
 | 
				
			|||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Immich
 | 
				
			||||||
 | 
					 * Immich API
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The version of the OpenAPI document: 1.17.0
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 | 
				
			||||||
 | 
					 * https://openapi-generator.tech
 | 
				
			||||||
 | 
					 * Do not edit the class manually.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface ConfigurationParameters {
 | 
				
			||||||
 | 
					    apiKey?: string | Promise<string> | ((name: string) => string) | ((name: string) => Promise<string>);
 | 
				
			||||||
 | 
					    username?: string;
 | 
				
			||||||
 | 
					    password?: string;
 | 
				
			||||||
 | 
					    accessToken?: string | Promise<string> | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise<string>);
 | 
				
			||||||
 | 
					    basePath?: string;
 | 
				
			||||||
 | 
					    baseOptions?: any;
 | 
				
			||||||
 | 
					    formDataCtor?: new () => any;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class Configuration {
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * parameter for apiKey security
 | 
				
			||||||
 | 
					     * @param name security name
 | 
				
			||||||
 | 
					     * @memberof Configuration
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    apiKey?: string | Promise<string> | ((name: string) => string) | ((name: string) => Promise<string>);
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * parameter for basic security
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @type {string}
 | 
				
			||||||
 | 
					     * @memberof Configuration
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    username?: string;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * parameter for basic security
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @type {string}
 | 
				
			||||||
 | 
					     * @memberof Configuration
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    password?: string;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * parameter for oauth2 security
 | 
				
			||||||
 | 
					     * @param name security name
 | 
				
			||||||
 | 
					     * @param scopes oauth2 scope
 | 
				
			||||||
 | 
					     * @memberof Configuration
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    accessToken?: string | Promise<string> | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise<string>);
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * override base path
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @type {string}
 | 
				
			||||||
 | 
					     * @memberof Configuration
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    basePath?: string;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * base options for axios calls
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @type {any}
 | 
				
			||||||
 | 
					     * @memberof Configuration
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    baseOptions?: any;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The FormData constructor that will be used to create multipart form data
 | 
				
			||||||
 | 
					     * requests. You can inject this here so that execution environments that
 | 
				
			||||||
 | 
					     * do not support the FormData class can still run the generated client.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @type {new () => FormData}
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    formDataCtor?: new () => any;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(param: ConfigurationParameters = {}) {
 | 
				
			||||||
 | 
					        this.apiKey = param.apiKey;
 | 
				
			||||||
 | 
					        this.username = param.username;
 | 
				
			||||||
 | 
					        this.password = param.password;
 | 
				
			||||||
 | 
					        this.accessToken = param.accessToken;
 | 
				
			||||||
 | 
					        this.basePath = param.basePath;
 | 
				
			||||||
 | 
					        this.baseOptions = param.baseOptions;
 | 
				
			||||||
 | 
					        this.formDataCtor = param.formDataCtor;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check if the given MIME is a JSON MIME.
 | 
				
			||||||
 | 
					     * JSON MIME examples:
 | 
				
			||||||
 | 
					     *   application/json
 | 
				
			||||||
 | 
					     *   application/json; charset=UTF8
 | 
				
			||||||
 | 
					     *   APPLICATION/JSON
 | 
				
			||||||
 | 
					     *   application/vnd.company+json
 | 
				
			||||||
 | 
					     * @param mime - MIME (Multipurpose Internet Mail Extensions)
 | 
				
			||||||
 | 
					     * @return True if the given MIME is JSON, false otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public isJsonMime(mime: string): boolean {
 | 
				
			||||||
 | 
					        const jsonMime: RegExp = new RegExp('^(application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(;.*)?$', 'i');
 | 
				
			||||||
 | 
					        return mime !== null && (jsonMime.test(mime) || mime.toLowerCase() === 'application/json-patch+json');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										57
									
								
								web/src/lib/open-api/git_push.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								web/src/lib/open-api/git_push.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
				
			|||||||
 | 
					#!/bin/sh
 | 
				
			||||||
 | 
					# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					git_user_id=$1
 | 
				
			||||||
 | 
					git_repo_id=$2
 | 
				
			||||||
 | 
					release_note=$3
 | 
				
			||||||
 | 
					git_host=$4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if [ "$git_host" = "" ]; then
 | 
				
			||||||
 | 
					    git_host="github.com"
 | 
				
			||||||
 | 
					    echo "[INFO] No command line input provided. Set \$git_host to $git_host"
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if [ "$git_user_id" = "" ]; then
 | 
				
			||||||
 | 
					    git_user_id="GIT_USER_ID"
 | 
				
			||||||
 | 
					    echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id"
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if [ "$git_repo_id" = "" ]; then
 | 
				
			||||||
 | 
					    git_repo_id="GIT_REPO_ID"
 | 
				
			||||||
 | 
					    echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id"
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if [ "$release_note" = "" ]; then
 | 
				
			||||||
 | 
					    release_note="Minor update"
 | 
				
			||||||
 | 
					    echo "[INFO] No command line input provided. Set \$release_note to $release_note"
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Initialize the local directory as a Git repository
 | 
				
			||||||
 | 
					git init
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Adds the files in the local repository and stages them for commit.
 | 
				
			||||||
 | 
					git add .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Commits the tracked changes and prepares them to be pushed to a remote repository.
 | 
				
			||||||
 | 
					git commit -m "$release_note"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Sets the new remote
 | 
				
			||||||
 | 
					git_remote=$(git remote)
 | 
				
			||||||
 | 
					if [ "$git_remote" = "" ]; then # git remote not defined
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if [ "$GIT_TOKEN" = "" ]; then
 | 
				
			||||||
 | 
					        echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment."
 | 
				
			||||||
 | 
					        git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					git pull origin master
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Pushes (Forces) the changes in the local repository up to the remote repository
 | 
				
			||||||
 | 
					echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git"
 | 
				
			||||||
 | 
					git push origin master 2>&1 | grep -v 'To https'
 | 
				
			||||||
							
								
								
									
										18
									
								
								web/src/lib/open-api/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								web/src/lib/open-api/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Immich
 | 
				
			||||||
 | 
					 * Immich API
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The version of the OpenAPI document: 1.17.0
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 | 
				
			||||||
 | 
					 * https://openapi-generator.tech
 | 
				
			||||||
 | 
					 * Do not edit the class manually.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export * from "./api";
 | 
				
			||||||
 | 
					export * from "./configuration";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -3,7 +3,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	import type { Load } from '@sveltejs/kit';
 | 
						import type { Load } from '@sveltejs/kit';
 | 
				
			||||||
	import { getAssetsInfo } from '$lib/stores/assets';
 | 
						import { getAssetsInfo } from '$lib/stores/assets';
 | 
				
			||||||
	import { checkAppVersion } from '$lib/utils/check-app-version';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	export const load: Load = async ({ session }) => {
 | 
						export const load: Load = async ({ session }) => {
 | 
				
			||||||
		if (!session.user) {
 | 
							if (!session.user) {
 | 
				
			||||||
@@ -25,7 +24,7 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
	import type { ImmichUser } from '$lib/models/immich-user';
 | 
						import type { ImmichUser } from '$lib/models/immich-user';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	import NavigationBar from '../../lib/components/shared/navigation-bar.svelte';
 | 
						import NavigationBar from '$lib/components/shared/navigation-bar.svelte';
 | 
				
			||||||
	import SideBarButton from '$lib/components/shared/side-bar-button.svelte';
 | 
						import SideBarButton from '$lib/components/shared/side-bar-button.svelte';
 | 
				
			||||||
	import CheckCircle from 'svelte-material-icons/CheckCircle.svelte';
 | 
						import CheckCircle from 'svelte-material-icons/CheckCircle.svelte';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -35,16 +34,16 @@
 | 
				
			|||||||
	import { fly } from 'svelte/transition';
 | 
						import { fly } from 'svelte/transition';
 | 
				
			||||||
	import { session } from '$app/stores';
 | 
						import { session } from '$app/stores';
 | 
				
			||||||
	import { assetsGroupByDate, flattenAssetGroupByDate } from '$lib/stores/assets';
 | 
						import { assetsGroupByDate, flattenAssetGroupByDate } from '$lib/stores/assets';
 | 
				
			||||||
	import ImmichThumbnail from '../../lib/components/asset-viewer/immich-thumbnail.svelte';
 | 
						import ImmichThumbnail from '$lib/components/asset-viewer/immich-thumbnail.svelte';
 | 
				
			||||||
	import moment from 'moment';
 | 
						import moment from 'moment';
 | 
				
			||||||
	import type { ImmichAsset } from '../../lib/models/immich-asset';
 | 
						import type { ImmichAsset } from '$lib/models/immich-asset';
 | 
				
			||||||
	import AssetViewer from '../../lib/components/asset-viewer/asset-viewer.svelte';
 | 
						import AssetViewer from '$lib/components/asset-viewer/asset-viewer.svelte';
 | 
				
			||||||
	import DownloadPanel from '../../lib/components/asset-viewer/download-panel.svelte';
 | 
						import StatusBox from '$lib/components/shared/status-box.svelte';
 | 
				
			||||||
	import StatusBox from '../../lib/components/shared/status-box.svelte';
 | 
						import { fileUploader } from '$lib/utils/file-uploader';
 | 
				
			||||||
	import { fileUploader } from '../../lib/utils/file-uploader';
 | 
						import { openWebsocketConnection, closeWebsocketConnection } from '$lib/stores/websocket';
 | 
				
			||||||
	import { openWebsocketConnection, closeWebsocketConnection } from '../../lib/stores/websocket';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	export let user: ImmichUser;
 | 
						export let user: ImmichUser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let selectedAction: AppSideBarSelection;
 | 
						let selectedAction: AppSideBarSelection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let selectedGroupThumbnail: number | null;
 | 
						let selectedGroupThumbnail: number | null;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,5 +16,7 @@
 | 
				
			|||||||
    "sourceMap": true,
 | 
					    "sourceMap": true,
 | 
				
			||||||
    "strict": true,
 | 
					    "strict": true,
 | 
				
			||||||
    "target": "es2020",
 | 
					    "target": "es2020",
 | 
				
			||||||
  }
 | 
					    "importsNotUsedAsValues": "preserve",
 | 
				
			||||||
 | 
					    "preserveValueImports": false
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user