feat(server,web): Delete and restore user from the admin portal (#935)

* delete and restore user from admin UI

* addressed review comments and fix e2e test

* added cron job to delete user, and some formatting changes

* addressed review comments

* adding missing queue registration
This commit is contained in:
Zeeshan Khan
2022-11-07 16:53:47 -05:00
committed by GitHub
parent 948ff5530c
commit fe4b307fe6
30 changed files with 804 additions and 59 deletions

View File

@@ -0,0 +1,39 @@
import { AssetEntity } from '@app/database/entities/asset.entity';
import { AssetResponseDto } from 'apps/immich/src/api-v1/asset/response-dto/asset-response.dto';
import fs from 'fs';
const deleteFiles = (asset: AssetEntity | AssetResponseDto) => {
fs.unlink(asset.originalPath, (err) => {
if (err) {
console.log('error deleting ', asset.originalPath);
}
});
// TODO: what if there is no asset.resizePath. Should fail the Job?
// => panoti report: Job not fail
if (asset.resizePath) {
fs.unlink(asset.resizePath, (err) => {
if (err) {
console.log('error deleting ', asset.resizePath);
}
});
}
if (asset.webpPath) {
fs.unlink(asset.webpPath, (err) => {
if (err) {
console.log('error deleting ', asset.webpPath);
}
});
}
if (asset.encodedVideoPath) {
fs.unlink(asset.encodedVideoPath, (err) => {
if (err) {
console.log('error deleting ', asset.encodedVideoPath);
}
});
}
};
export const assetUtils = { deleteFiles };

View File

@@ -1 +1,3 @@
export * from './time-utils';
export * from './asset-utils';
export * from './user-utils';

View File

@@ -0,0 +1,19 @@
// create unit test for user utils
import { UserEntity } from '@app/database/entities/user.entity';
import { userUtils } from './user-utils';
describe('User Utilities', () => {
describe('checkIsReadyForDeletion', () => {
it('check that user is not ready to be deleted', () => {
const result = userUtils.isReadyForDeletion({ deletedAt: new Date() } as UserEntity);
expect(result).toBeFalsy();
});
it('check that user is ready to be deleted', () => {
const aWeekAgo = new Date(new Date().getTime() - 8 * 86400000);
const result = userUtils.isReadyForDeletion({ deletedAt: aWeekAgo } as UserEntity);
expect(result).toBeTruthy();
});
});
});

View File

@@ -0,0 +1,16 @@
import { UserEntity } from '@app/database/entities/user.entity';
function createUserUtils() {
const isReadyForDeletion = (user: UserEntity): boolean => {
if (user.deletedAt == null) return false;
const millisecondsInDay = 86400000;
// get this number (7 days) from some configuration perhaps ?
const millisecondsDeleteWait = millisecondsInDay * 7;
const millisecondsSinceDelete = new Date().getTime() - (user.deletedAt?.getTime() ?? 0);
return millisecondsSinceDelete >= millisecondsDeleteWait;
};
return { isReadyForDeletion };
}
export const userUtils = createUserUtils();

View File

@@ -1,4 +1,4 @@
import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn } from 'typeorm';
import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, DeleteDateColumn } from 'typeorm';
@Entity('users')
export class UserEntity {
@@ -31,4 +31,7 @@ export class UserEntity {
@CreateDateColumn()
createdAt!: string;
@DeleteDateColumn()
deletedAt?: Date;
}

View File

@@ -0,0 +1,13 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddingDeletedAtColumnInUserEntity1667762360744 implements MigrationInterface {
name = 'AddingDeletedAtColumnInUserEntity1667762360744';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "users" ADD "deletedAt" TIMESTAMP`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "deletedAt"`);
}
}

View File

@@ -29,3 +29,8 @@ export enum MachineLearningJobNameEnum {
OBJECT_DETECTION = 'detect-object',
IMAGE_TAGGING = 'tag-image',
}
/**
* User deletion Queue Jobs
*/
export const userDeletionProcessorName = 'user-deletion';

View File

@@ -5,4 +5,5 @@ export enum QueueNameEnum {
CHECKSUM_GENERATION = 'generate-checksum-queue',
ASSET_UPLOADED = 'asset-uploaded-queue',
MACHINE_LEARNING = 'machine-learning-queue',
USER_DELETION = 'user-deletion-queue',
}

View File

@@ -0,0 +1,8 @@
import { UserEntity } from '@app/database/entities/user.entity';
export interface IUserDeletionJob {
/**
* The user entity that was saved in the database
*/
user: UserEntity;
}