You've already forked immich
mirror of
https://github.com/immich-app/immich.git
synced 2025-08-09 23:17:29 +02:00
Refactor mobile to use OpenApi generated SDK (#336)
This commit is contained in:
@@ -19,11 +19,10 @@ import {
|
||||
} from '@nestjs/common';
|
||||
import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard';
|
||||
import { AssetService } from './asset.service';
|
||||
import { FileFieldsInterceptor, FileInterceptor } from '@nestjs/platform-express';
|
||||
import { FileInterceptor } from '@nestjs/platform-express';
|
||||
import { assetUploadOption } from '../../config/asset-upload.config';
|
||||
import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
|
||||
import { ServeFileDto } from './dto/serve-file.dto';
|
||||
import { AssetEntity } from '@app/database/entities/asset.entity';
|
||||
import { Response as Res } from 'express';
|
||||
import { BackgroundTaskService } from '../../modules/background-task/background-task.service';
|
||||
import { DeleteAssetDto } from './dto/delete-asset.dto';
|
||||
@@ -43,6 +42,7 @@ import { CheckDuplicateAssetResponseDto } from './response-dto/check-duplicate-a
|
||||
import { AssetFileUploadDto } from './dto/asset-file-upload.dto';
|
||||
import { CreateAssetDto } from './dto/create-asset.dto';
|
||||
import { AssetFileUploadResponseDto } from './response-dto/asset-file-upload-response.dto';
|
||||
import { DeleteAssetResponseDto, DeleteAssetStatusEnum } from './response-dto/delete-asset-response.dto';
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@@ -124,7 +124,7 @@ export class AssetController {
|
||||
}
|
||||
|
||||
@Get('/searchTerm')
|
||||
async getAssetSearchTerms(@GetAuthUser() authUser: AuthUserDto): Promise<String[]> {
|
||||
async getAssetSearchTerms(@GetAuthUser() authUser: AuthUserDto): Promise<string[]> {
|
||||
return this.assetService.getAssetSearchTerm(authUser);
|
||||
}
|
||||
|
||||
@@ -164,7 +164,10 @@ export class AssetController {
|
||||
}
|
||||
|
||||
@Delete('/')
|
||||
async deleteAsset(@GetAuthUser() authUser: AuthUserDto, @Body(ValidationPipe) assetIds: DeleteAssetDto) {
|
||||
async deleteAsset(
|
||||
@GetAuthUser() authUser: AuthUserDto,
|
||||
@Body(ValidationPipe) assetIds: DeleteAssetDto,
|
||||
): Promise<DeleteAssetResponseDto[]> {
|
||||
const deleteAssetList: AssetResponseDto[] = [];
|
||||
|
||||
for (const id of assetIds.ids) {
|
||||
@@ -178,7 +181,7 @@ export class AssetController {
|
||||
const result = await this.assetService.deleteAssetById(authUser, assetIds);
|
||||
|
||||
result.forEach((res) => {
|
||||
deleteAssetList.filter((a) => a.id == res.id && res.status == 'success');
|
||||
deleteAssetList.filter((a) => a.id == res.id && res.status == DeleteAssetStatusEnum.SUCCESS);
|
||||
});
|
||||
|
||||
await this.backgroundTaskService.deleteFileOnDisk(deleteAssetList);
|
||||
|
@@ -22,6 +22,7 @@ import { CuratedObjectsResponseDto } from './response-dto/curated-objects-respon
|
||||
import { AssetResponseDto, mapAsset } from './response-dto/asset-response.dto';
|
||||
import { AssetFileUploadDto } from './dto/asset-file-upload.dto';
|
||||
import { CreateAssetDto } from './dto/create-asset.dto';
|
||||
import { DeleteAssetResponseDto, DeleteAssetStatusEnum } from './response-dto/delete-asset-response.dto';
|
||||
|
||||
const fileInfo = promisify(stat);
|
||||
|
||||
@@ -280,7 +281,7 @@ export class AssetService {
|
||||
|
||||
return new StreamableFile(fileReadStream);
|
||||
} catch (e) {
|
||||
Logger.error(`Cannot create read stream for asset ${asset.id}`, 'serveFile[IMAGE]');
|
||||
Logger.error(`Cannot create read stream for asset ${asset.id} ${JSON.stringify(e)}`, 'serveFile[IMAGE]');
|
||||
throw new InternalServerErrorException(
|
||||
e,
|
||||
`Cannot read thumbnail file for asset ${asset.id} - contact your administrator`,
|
||||
@@ -354,8 +355,8 @@ export class AssetService {
|
||||
}
|
||||
}
|
||||
|
||||
public async deleteAssetById(authUser: AuthUserDto, assetIds: DeleteAssetDto) {
|
||||
const result = [];
|
||||
public async deleteAssetById(authUser: AuthUserDto, assetIds: DeleteAssetDto): Promise<DeleteAssetResponseDto[]> {
|
||||
const result: DeleteAssetResponseDto[] = [];
|
||||
|
||||
const target = assetIds.ids;
|
||||
for (const assetId of target) {
|
||||
@@ -367,12 +368,12 @@ export class AssetService {
|
||||
if (res.affected) {
|
||||
result.push({
|
||||
id: assetId,
|
||||
status: 'success',
|
||||
status: DeleteAssetStatusEnum.SUCCESS,
|
||||
});
|
||||
} else {
|
||||
result.push({
|
||||
id: assetId,
|
||||
status: 'failed',
|
||||
status: DeleteAssetStatusEnum.FAILED,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,5 @@
|
||||
import { AssetType } from '@app/database/entities/asset.entity';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsEnum, IsNotEmpty, IsOptional } from 'class-validator';
|
||||
import { CreateAssetDto } from './create-asset.dto';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
|
||||
export class AssetFileUploadDto {
|
||||
@IsNotEmpty()
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { IsNotEmpty, IsOptional } from 'class-validator';
|
||||
import { AssetType } from '@app/database/entities/asset.entity';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class CreateAssetDto {
|
||||
@IsNotEmpty()
|
||||
@@ -9,6 +10,7 @@ export class CreateAssetDto {
|
||||
deviceId!: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@ApiProperty({ enumName: 'AssetTypeEnum', enum: AssetType })
|
||||
assetType!: AssetType;
|
||||
|
||||
@IsNotEmpty()
|
||||
|
@@ -1,6 +0,0 @@
|
||||
import { IsOptional } from 'class-validator';
|
||||
|
||||
export class GetAllAssetQueryDto {
|
||||
@IsOptional()
|
||||
nextPageKey?: string;
|
||||
}
|
@@ -1,8 +0,0 @@
|
||||
import { AssetEntity } from '@app/database/entities/asset.entity';
|
||||
|
||||
// TODO: this doesn't seem to be used
|
||||
export class GetAllAssetReponseDto {
|
||||
data!: Array<{ date: string; assets: Array<AssetEntity> }>;
|
||||
count!: number;
|
||||
nextPageKey!: string;
|
||||
}
|
@@ -1,6 +0,0 @@
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
|
||||
export class GetNewAssetQueryDto {
|
||||
@IsNotEmpty()
|
||||
latestDate!: string;
|
||||
}
|
@@ -1,4 +0,0 @@
|
||||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { CreateAssetDto } from './create-asset.dto';
|
||||
|
||||
export class UpdateAssetDto extends PartialType(CreateAssetDto) {}
|
@@ -1,4 +0,0 @@
|
||||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { CreateExifDto } from './create-exif.dto';
|
||||
|
||||
export class UpdateExifDto extends PartialType(CreateExifDto) {}
|
@@ -1,4 +1,5 @@
|
||||
import { AssetEntity, AssetType } from '@app/database/entities/asset.entity';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { ExifResponseDto, mapExif } from './exif-response.dto';
|
||||
import { SmartInfoResponseDto, mapSmartInfo } from './smart-info-response.dto';
|
||||
|
||||
@@ -7,6 +8,8 @@ export class AssetResponseDto {
|
||||
deviceAssetId!: string;
|
||||
ownerId!: string;
|
||||
deviceId!: string;
|
||||
|
||||
@ApiProperty({ enumName: 'AssetTypeEnum', enum: AssetType })
|
||||
type!: AssetType;
|
||||
originalPath!: string;
|
||||
resizePath!: string | null;
|
||||
|
@@ -0,0 +1,13 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export enum DeleteAssetStatusEnum {
|
||||
SUCCESS = 'SUCCESS',
|
||||
FAILED = 'FAILED',
|
||||
}
|
||||
|
||||
export class DeleteAssetResponseDto {
|
||||
id!: string;
|
||||
|
||||
@ApiProperty({ type: 'string', enum: DeleteAssetStatusEnum, enumName: 'DeleteAssetStatus' })
|
||||
status!: DeleteAssetStatusEnum;
|
||||
}
|
@@ -1,26 +1,26 @@
|
||||
import { ExifEntity } from '@app/database/entities/exif.entity';
|
||||
|
||||
export class ExifResponseDto {
|
||||
id!: string;
|
||||
make: string | null = null;
|
||||
model: string | null = null;
|
||||
imageName: string | null = null;
|
||||
exifImageWidth: number | null = null;
|
||||
exifImageHeight: number | null = null;
|
||||
fileSizeInByte: number | null = null;
|
||||
orientation: string | null = null;
|
||||
dateTimeOriginal: Date | null = null;
|
||||
modifyDate: Date | null = null;
|
||||
lensModel: string | null = null;
|
||||
fNumber: number | null = null;
|
||||
focalLength: number | null = null;
|
||||
iso: number | null = null;
|
||||
exposureTime: number | null = null;
|
||||
latitude: number | null = null;
|
||||
longitude: number | null = null;
|
||||
city: string | null = null;
|
||||
state: string | null = null;
|
||||
country: string | null = null;
|
||||
id?: string | null = null;
|
||||
make?: string | null = null;
|
||||
model?: string | null = null;
|
||||
imageName?: string | null = null;
|
||||
exifImageWidth?: number | null = null;
|
||||
exifImageHeight?: number | null = null;
|
||||
fileSizeInByte?: number | null = null;
|
||||
orientation?: string | null = null;
|
||||
dateTimeOriginal?: Date | null = null;
|
||||
modifyDate?: Date | null = null;
|
||||
lensModel?: string | null = null;
|
||||
fNumber?: number | null = null;
|
||||
focalLength?: number | null = null;
|
||||
iso?: number | null = null;
|
||||
exposureTime?: number | null = null;
|
||||
latitude?: number | null = null;
|
||||
longitude?: number | null = null;
|
||||
city?: string | null = null;
|
||||
state?: string | null = null;
|
||||
country?: string | null = null;
|
||||
}
|
||||
|
||||
export function mapExif(entity: ExifEntity): ExifResponseDto {
|
||||
|
@@ -1,7 +1,10 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class ValidateAccessTokenResponseDto {
|
||||
constructor(authStatus: boolean) {
|
||||
this.authStatus = authStatus;
|
||||
}
|
||||
|
||||
authStatus: boolean;
|
||||
@ApiProperty({ type: 'boolean' })
|
||||
authStatus!: boolean;
|
||||
}
|
||||
|
@@ -1,11 +1,13 @@
|
||||
import { IsNotEmpty, IsOptional } from 'class-validator';
|
||||
import { DeviceType } from '@app/database/entities/device-info.entity';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class CreateDeviceInfoDto {
|
||||
@IsNotEmpty()
|
||||
deviceId!: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@ApiProperty({ enumName: 'DeviceTypeEnum', enum: DeviceType })
|
||||
deviceType!: DeviceType;
|
||||
|
||||
@IsOptional()
|
||||
|
@@ -1,4 +1,17 @@
|
||||
import { DeviceType } from '@app/database/entities/device-info.entity';
|
||||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty, IsOptional } from 'class-validator';
|
||||
import { CreateDeviceInfoDto } from './create-device-info.dto';
|
||||
|
||||
export class UpdateDeviceInfoDto extends PartialType(CreateDeviceInfoDto) {}
|
||||
export class UpdateDeviceInfoDto {
|
||||
@IsNotEmpty()
|
||||
deviceId!: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@ApiProperty({ enumName: 'DeviceTypeEnum', enum: DeviceType })
|
||||
deviceType!: DeviceType;
|
||||
|
||||
@IsOptional()
|
||||
isAutoBackup?: boolean;
|
||||
}
|
||||
|
@@ -1,11 +1,15 @@
|
||||
import { DeviceInfoEntity, DeviceType } from '@app/database/entities/device-info.entity';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class DeviceInfoResponseDto {
|
||||
@ApiProperty({ type: 'integer' })
|
||||
id!: number;
|
||||
userId!: string;
|
||||
deviceId!: string;
|
||||
|
||||
@ApiProperty({ enumName: 'DeviceTypeEnum', enum: DeviceType })
|
||||
deviceType!: DeviceType;
|
||||
notificationToken!: string | null;
|
||||
|
||||
createdAt!: string;
|
||||
isAutoBackup!: boolean;
|
||||
}
|
||||
@@ -16,7 +20,6 @@ export function mapDeviceInfoResponse(entity: DeviceInfoEntity): DeviceInfoRespo
|
||||
userId: entity.userId,
|
||||
deviceId: entity.deviceId,
|
||||
deviceType: entity.deviceType,
|
||||
notificationToken: entity.notificationToken,
|
||||
createdAt: entity.createdAt,
|
||||
isAutoBackup: entity.isAutoBackup,
|
||||
};
|
||||
|
@@ -1,9 +1,19 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class ServerInfoResponseDto {
|
||||
diskSize!: string;
|
||||
diskUse!: string;
|
||||
diskAvailable!: string;
|
||||
|
||||
@ApiProperty({ type: 'integer' })
|
||||
diskSizeRaw!: number;
|
||||
|
||||
@ApiProperty({ type: 'integer' })
|
||||
diskUseRaw!: number;
|
||||
|
||||
@ApiProperty({ type: 'integer' })
|
||||
diskAvailableRaw!: number;
|
||||
|
||||
@ApiProperty({ type: 'number', format: 'float' })
|
||||
diskUsagePercentage!: number;
|
||||
}
|
||||
|
@@ -1,8 +1,13 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IServerVersion } from 'apps/immich/src/constants/server_version.constant';
|
||||
|
||||
export class ServerVersionReponseDto implements IServerVersion {
|
||||
@ApiProperty({ type: 'integer' })
|
||||
major!: number;
|
||||
@ApiProperty({ type: 'integer' })
|
||||
minor!: number;
|
||||
@ApiProperty({ type: 'integer' })
|
||||
patch!: number;
|
||||
@ApiProperty({ type: 'integer' })
|
||||
build!: number;
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@ import { APP_UPLOAD_LOCATION } from '../../constants/upload_location.constant';
|
||||
|
||||
@Injectable()
|
||||
export class ServerInfoService {
|
||||
async getServerInfo() {
|
||||
async getServerInfo(): Promise<ServerInfoResponseDto> {
|
||||
const diskInfo = await diskusage.check(APP_UPLOAD_LOCATION);
|
||||
|
||||
const usagePercentage = (((diskInfo.total - diskInfo.free) / diskInfo.total) * 100).toFixed(2);
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Express } from 'express';
|
||||
|
||||
export class CreateProfileImageDto {
|
||||
@ApiProperty({ type: 'string', format: 'binary' })
|
||||
file: any;
|
||||
file!: Express.Multer.File;
|
||||
}
|
||||
|
@@ -1,5 +1,7 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class UserCountResponseDto {
|
||||
@ApiProperty({ type: 'integer' })
|
||||
userCount!: number;
|
||||
}
|
||||
|
||||
@@ -7,4 +9,4 @@ export function mapUserCountResponse(count: number): UserCountResponseDto {
|
||||
return {
|
||||
userCount: count,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@ import {
|
||||
UseInterceptors,
|
||||
UploadedFile,
|
||||
Response,
|
||||
Request,
|
||||
StreamableFile,
|
||||
ParseBoolPipe,
|
||||
} from '@nestjs/common';
|
||||
@@ -22,7 +23,7 @@ import { AdminRolesGuard } from '../../middlewares/admin-role-guard.middleware';
|
||||
import { UpdateUserDto } from './dto/update-user.dto';
|
||||
import { FileInterceptor } from '@nestjs/platform-express';
|
||||
import { profileImageUploadOption } from '../../config/profile-image-upload.config';
|
||||
import { Response as Res } from 'express';
|
||||
import { Response as Res, Request as Req } from 'express';
|
||||
import { ApiBearerAuth, ApiBody, ApiConsumes, ApiTags } from '@nestjs/swagger';
|
||||
import { UserResponseDto } from './response-dto/user-response.dto';
|
||||
import { UserCountResponseDto } from './response-dto/user-count-response.dto';
|
||||
@@ -76,13 +77,16 @@ export class UserController {
|
||||
@ApiBearerAuth()
|
||||
@ApiConsumes('multipart/form-data')
|
||||
@ApiBody({
|
||||
description: 'A new avatar for the user',
|
||||
type: CreateProfileImageDto,
|
||||
})
|
||||
@Post('/profile-image')
|
||||
async createProfileImage(
|
||||
@GetAuthUser() authUser: AuthUserDto,
|
||||
@UploadedFile() fileInfo: Express.Multer.File,
|
||||
@Request() req: Req,
|
||||
): Promise<CreateProfileImageResponseDto> {
|
||||
console.log(req.body, req.file);
|
||||
return await this.userService.createProfileImage(authUser, fileInfo);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user