mirror of https://github.com/immich-app/immich.git
feat(server): harden move file (#4361)
Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>pull/4409/head
parent
332a8d80f2
commit
09bf1c9175
@ -0,0 +1,12 @@
|
|||||||
|
import { MoveEntity, PathType } from '@app/infra/entities';
|
||||||
|
|
||||||
|
export const IMoveRepository = 'IMoveRepository';
|
||||||
|
|
||||||
|
export type MoveCreate = Pick<MoveEntity, 'oldPath' | 'newPath' | 'entityId' | 'pathType'> & Partial<MoveEntity>;
|
||||||
|
|
||||||
|
export interface IMoveRepository {
|
||||||
|
create(entity: MoveCreate): Promise<MoveEntity>;
|
||||||
|
getByEntity(entityId: string, pathType: PathType): Promise<MoveEntity | null>;
|
||||||
|
update(entity: Partial<MoveEntity>): Promise<MoveEntity>;
|
||||||
|
delete(move: MoveEntity): Promise<MoveEntity>;
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
import { Column, Entity, PrimaryGeneratedColumn, Unique } from 'typeorm';
|
||||||
|
|
||||||
|
@Entity('move_history')
|
||||||
|
// path lock (per entity)
|
||||||
|
@Unique('UQ_entityId_pathType', ['entityId', 'pathType'])
|
||||||
|
// new path lock (global)
|
||||||
|
@Unique('UQ_newPath', ['newPath'])
|
||||||
|
export class MoveEntity {
|
||||||
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
id!: string;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar' })
|
||||||
|
entityId!: string;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar' })
|
||||||
|
pathType!: PathType;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar' })
|
||||||
|
oldPath!: string;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar' })
|
||||||
|
newPath!: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum AssetPathType {
|
||||||
|
ORIGINAL = 'original',
|
||||||
|
JPEG_THUMBNAIL = 'jpeg_thumbnail',
|
||||||
|
WEBP_THUMBNAIL = 'webp_thumbnail',
|
||||||
|
ENCODED_VIDEO = 'encoded_video',
|
||||||
|
SIDECAR = 'sidecar',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum PersonPathType {
|
||||||
|
FACE = 'face',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PathType = AssetPathType | PersonPathType;
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class AddMoveTable1696968880063 implements MigrationInterface {
|
||||||
|
name = 'AddMoveTable1696968880063'
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`CREATE TABLE "move_history" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "entityId" character varying NOT NULL, "pathType" character varying NOT NULL, "oldPath" character varying NOT NULL, "newPath" character varying NOT NULL, CONSTRAINT "UQ_newPath" UNIQUE ("newPath"), CONSTRAINT "UQ_entityId_pathType" UNIQUE ("entityId", "pathType"), CONSTRAINT "PK_af608f132233acf123f2949678d" PRIMARY KEY ("id"))`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`DROP TABLE "move_history"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
import { IMoveRepository, MoveCreate } from '@app/domain';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
import { MoveEntity, PathType } from '../entities';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MoveRepository implements IMoveRepository {
|
||||||
|
constructor(@InjectRepository(MoveEntity) private repository: Repository<MoveEntity>) {}
|
||||||
|
|
||||||
|
create(entity: MoveCreate): Promise<MoveEntity> {
|
||||||
|
return this.repository.save(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
getByEntity(entityId: string, pathType: PathType): Promise<MoveEntity | null> {
|
||||||
|
return this.repository.findOne({ where: { entityId, pathType } });
|
||||||
|
}
|
||||||
|
|
||||||
|
update(entity: Partial<MoveEntity>): Promise<MoveEntity> {
|
||||||
|
return this.repository.save(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(move: MoveEntity): Promise<MoveEntity> {
|
||||||
|
return this.repository.remove(move);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
import { IMoveRepository } from '@app/domain';
|
||||||
|
|
||||||
|
export const newMoveRepositoryMock = (): jest.Mocked<IMoveRepository> => {
|
||||||
|
return {
|
||||||
|
create: jest.fn(),
|
||||||
|
getByEntity: jest.fn(),
|
||||||
|
update: jest.fn(),
|
||||||
|
delete: jest.fn(),
|
||||||
|
};
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue