chore(web): quota enhancement (#6371)
* chore(web): quota enhancement * show quota in user table * update quota for single user ioption * Add a note how to set unlimited storage * fixed deletion doesn't update quota * refactor relation * fixed test * re-refactor * update sql * fix e2e test * Update server/src/domain/user/user.service.ts Co-authored-by: Jason Rasmussen <jrasm91@gmail.com> * revert e2e test --------- Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
This commit is contained in:
@@ -845,7 +845,16 @@ describe(AssetService.name, () => {
|
||||
it('should remove faces', async () => {
|
||||
const assetWithFace = { ...assetStub.image, faces: [faceStub.face1, faceStub.mergeFace1] };
|
||||
|
||||
when(assetMock.getById).calledWith(assetWithFace.id).mockResolvedValue(assetWithFace);
|
||||
when(assetMock.getById)
|
||||
.calledWith(assetWithFace.id, {
|
||||
faces: {
|
||||
person: true,
|
||||
},
|
||||
library: true,
|
||||
stack: true,
|
||||
exifInfo: true,
|
||||
})
|
||||
.mockResolvedValue(assetWithFace);
|
||||
|
||||
await sut.handleAssetDeletion({ id: assetWithFace.id });
|
||||
|
||||
@@ -870,7 +879,16 @@ describe(AssetService.name, () => {
|
||||
});
|
||||
|
||||
it('should update stack parent if asset has stack children', async () => {
|
||||
when(assetMock.getById).calledWith(assetStub.primaryImage.id).mockResolvedValue(assetStub.primaryImage);
|
||||
when(assetMock.getById)
|
||||
.calledWith(assetStub.primaryImage.id, {
|
||||
faces: {
|
||||
person: true,
|
||||
},
|
||||
library: true,
|
||||
stack: true,
|
||||
exifInfo: true,
|
||||
})
|
||||
.mockResolvedValue(assetStub.primaryImage);
|
||||
|
||||
await sut.handleAssetDeletion({ id: assetStub.primaryImage.id });
|
||||
|
||||
@@ -883,7 +901,16 @@ describe(AssetService.name, () => {
|
||||
});
|
||||
|
||||
it('should not schedule delete-files job for readonly assets', async () => {
|
||||
when(assetMock.getById).calledWith(assetStub.readOnly.id).mockResolvedValue(assetStub.readOnly);
|
||||
when(assetMock.getById)
|
||||
.calledWith(assetStub.readOnly.id, {
|
||||
faces: {
|
||||
person: true,
|
||||
},
|
||||
library: true,
|
||||
stack: true,
|
||||
exifInfo: true,
|
||||
})
|
||||
.mockResolvedValue(assetStub.readOnly);
|
||||
|
||||
await sut.handleAssetDeletion({ id: assetStub.readOnly.id });
|
||||
|
||||
@@ -903,7 +930,16 @@ describe(AssetService.name, () => {
|
||||
});
|
||||
|
||||
it('should process assets from external library with fromExternal flag', async () => {
|
||||
when(assetMock.getById).calledWith(assetStub.external.id).mockResolvedValue(assetStub.external);
|
||||
when(assetMock.getById)
|
||||
.calledWith(assetStub.external.id, {
|
||||
faces: {
|
||||
person: true,
|
||||
},
|
||||
library: true,
|
||||
stack: true,
|
||||
exifInfo: true,
|
||||
})
|
||||
.mockResolvedValue(assetStub.external);
|
||||
|
||||
await sut.handleAssetDeletion({ id: assetStub.external.id, fromExternal: true });
|
||||
|
||||
@@ -926,6 +962,27 @@ describe(AssetService.name, () => {
|
||||
});
|
||||
|
||||
it('should delete a live photo', async () => {
|
||||
when(assetMock.getById)
|
||||
.calledWith(assetStub.livePhotoStillAsset.id, {
|
||||
faces: {
|
||||
person: true,
|
||||
},
|
||||
library: true,
|
||||
stack: true,
|
||||
exifInfo: true,
|
||||
})
|
||||
.mockResolvedValue(assetStub.livePhotoStillAsset);
|
||||
when(assetMock.getById)
|
||||
.calledWith(assetStub.livePhotoMotionAsset.id, {
|
||||
faces: {
|
||||
person: true,
|
||||
},
|
||||
library: true,
|
||||
stack: true,
|
||||
exifInfo: true,
|
||||
})
|
||||
.mockResolvedValue(assetStub.livePhotoMotionAsset);
|
||||
|
||||
await sut.handleAssetDeletion({ id: assetStub.livePhotoStillAsset.id });
|
||||
|
||||
expect(jobMock.queue.mock.calls).toEqual([
|
||||
@@ -950,7 +1007,16 @@ describe(AssetService.name, () => {
|
||||
});
|
||||
|
||||
it('should update usage', async () => {
|
||||
when(assetMock.getById).calledWith(assetStub.image.id).mockResolvedValue(assetStub.image);
|
||||
when(assetMock.getById)
|
||||
.calledWith(assetStub.image.id, {
|
||||
faces: {
|
||||
person: true,
|
||||
},
|
||||
library: true,
|
||||
stack: true,
|
||||
exifInfo: true,
|
||||
})
|
||||
.mockResolvedValue(assetStub.image);
|
||||
await sut.handleAssetDeletion({ id: assetStub.image.id });
|
||||
|
||||
expect(userMock.updateUsage).toHaveBeenCalledWith(assetStub.image.ownerId, -5000);
|
||||
@@ -1005,7 +1071,13 @@ describe(AssetService.name, () => {
|
||||
accessMock.asset.checkOwnerAccess.mockResolvedValueOnce(new Set(['new']));
|
||||
|
||||
when(assetMock.getById)
|
||||
.calledWith(assetStub.image.id)
|
||||
.calledWith(assetStub.image.id, {
|
||||
faces: {
|
||||
person: true,
|
||||
},
|
||||
library: true,
|
||||
stack: true,
|
||||
})
|
||||
.mockResolvedValue(assetStub.image as AssetEntity);
|
||||
|
||||
await sut.updateStackParent(authStub.user1, {
|
||||
@@ -1032,7 +1104,13 @@ describe(AssetService.name, () => {
|
||||
accessMock.asset.checkOwnerAccess.mockResolvedValueOnce(new Set([assetStub.primaryImage.id]));
|
||||
accessMock.asset.checkOwnerAccess.mockResolvedValueOnce(new Set(['new']));
|
||||
when(assetMock.getById)
|
||||
.calledWith(assetStub.primaryImage.id)
|
||||
.calledWith(assetStub.primaryImage.id, {
|
||||
faces: {
|
||||
person: true,
|
||||
},
|
||||
library: true,
|
||||
stack: true,
|
||||
})
|
||||
.mockResolvedValue(assetStub.primaryImage as AssetEntity);
|
||||
|
||||
await sut.updateStackParent(authStub.user1, {
|
||||
@@ -1042,7 +1120,9 @@ describe(AssetService.name, () => {
|
||||
|
||||
expect(assetMock.updateAll).toBeCalledWith(
|
||||
[assetStub.primaryImage.id, 'stack-child-asset-1', 'stack-child-asset-2'],
|
||||
{ stackParentId: 'new' },
|
||||
{
|
||||
stackParentId: 'new',
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -465,7 +465,15 @@ export class AssetService {
|
||||
async handleAssetDeletion(job: IAssetDeletionJob) {
|
||||
const { id, fromExternal } = job;
|
||||
|
||||
const asset = await this.assetRepository.getById(id);
|
||||
const asset = await this.assetRepository.getById(id, {
|
||||
faces: {
|
||||
person: true,
|
||||
},
|
||||
library: true,
|
||||
stack: true,
|
||||
exifInfo: true,
|
||||
});
|
||||
|
||||
if (!asset) {
|
||||
return false;
|
||||
}
|
||||
@@ -554,7 +562,13 @@ export class AssetService {
|
||||
await this.access.requirePermission(auth, Permission.ASSET_UPDATE, newParentId);
|
||||
|
||||
const childIds: string[] = [];
|
||||
const oldParent = await this.assetRepository.getById(oldParentId);
|
||||
const oldParent = await this.assetRepository.getById(oldParentId, {
|
||||
faces: {
|
||||
person: true,
|
||||
},
|
||||
library: true,
|
||||
stack: true,
|
||||
});
|
||||
if (oldParent != null) {
|
||||
childIds.push(oldParent.id);
|
||||
// Get all children of old parent
|
||||
|
||||
@@ -34,5 +34,5 @@ export interface IUserRepository {
|
||||
delete(user: UserEntity, hard?: boolean): Promise<UserEntity>;
|
||||
restore(user: UserEntity): Promise<UserEntity>;
|
||||
updateUsage(id: string, delta: number): Promise<void>;
|
||||
syncUsage(): Promise<void>;
|
||||
syncUsage(id?: string): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,12 @@ export class UserService {
|
||||
}
|
||||
|
||||
async update(auth: AuthDto, dto: UpdateUserDto): Promise<UserResponseDto> {
|
||||
await this.findOrFail(dto.id, {});
|
||||
const user = await this.findOrFail(dto.id, {});
|
||||
|
||||
if (dto.quotaSizeInBytes && user.quotaSizeInBytes !== dto.quotaSizeInBytes) {
|
||||
await this.userRepository.syncUsage(dto.id);
|
||||
}
|
||||
|
||||
return this.userCore.updateUser(auth.user, dto.id, dto).then(mapUser);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user