refactor(server): jobs and processors (#1787)

* refactor: jobs and processors

* refactor: storage migration processor

* fix: tests

* fix: code warning

* chore: ignore coverage from infra

* fix: sync move asset logic between job core and asset core

* refactor: move error handling inside of catch

* refactor(server): job core into dedicated service calls

* refactor: smart info

* fix: tests

* chore: smart info tests

* refactor: use asset repository

* refactor: thumbnail processor

* chore: coverage reqs
This commit is contained in:
Jason Rasmussen
2023-02-25 09:12:03 -05:00
committed by GitHub
parent 71d8567f18
commit 6c7679714b
108 changed files with 1645 additions and 1072 deletions
+76 -10
View File
@@ -1,26 +1,43 @@
import { BadRequestException, Inject, Injectable, NotFoundException } from '@nestjs/common';
import { UserEntity } from '@app/infra/db/entities';
import { BadRequestException, Inject, Injectable, Logger, NotFoundException } from '@nestjs/common';
import { randomBytes } from 'crypto';
import { ReadStream } from 'fs';
import { join } from 'path';
import { APP_UPLOAD_LOCATION } from '@app/common';
import { IAlbumRepository } from '../album/album.repository';
import { IKeyRepository } from '../api-key/api-key.repository';
import { IAssetRepository } from '../asset/asset.repository';
import { AuthUserDto } from '../auth';
import { ICryptoRepository } from '../crypto';
import { IUserRepository } from '../user';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { UserCountDto } from './dto/user-count.dto';
import { ICryptoRepository } from '../crypto/crypto.repository';
import { IJobRepository, IUserDeletionJob, JobName } from '../job';
import { IStorageRepository } from '../storage/storage.repository';
import { IUserTokenRepository } from '../user-token/user-token.repository';
import { IUserRepository } from '../user/user.repository';
import { CreateUserDto, UpdateUserDto, UserCountDto } from './dto';
import {
CreateProfileImageResponseDto,
mapCreateProfileImageResponse,
} from './response-dto/create-profile-image-response.dto';
import { mapUserCountResponse, UserCountResponseDto } from './response-dto/user-count-response.dto';
import { mapUser, UserResponseDto } from './response-dto/user-response.dto';
mapUser,
mapUserCountResponse,
UserCountResponseDto,
UserResponseDto,
} from './response-dto';
import { UserCore } from './user.core';
@Injectable()
export class UserService {
private logger = new Logger(UserService.name);
private userCore: UserCore;
constructor(
@Inject(IUserRepository) userRepository: IUserRepository,
@Inject(IUserRepository) private userRepository: IUserRepository,
@Inject(ICryptoRepository) cryptoRepository: ICryptoRepository,
@Inject(IAlbumRepository) private albumRepository: IAlbumRepository,
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
@Inject(IJobRepository) private jobRepository: IJobRepository,
@Inject(IKeyRepository) private keyRepository: IKeyRepository,
@Inject(IStorageRepository) private storageRepository: IStorageRepository,
@Inject(IUserTokenRepository) private tokenRepository: IUserTokenRepository,
) {
this.userCore = new UserCore(userRepository, cryptoRepository);
}
@@ -123,4 +140,53 @@ export class UserService {
return { admin, password, provided: !!providedPassword };
}
async handleUserDeleteCheck() {
const users = await this.userRepository.getDeletedUsers();
for (const user of users) {
if (this.isReadyForDeletion(user)) {
await this.jobRepository.queue({ name: JobName.USER_DELETION, data: { user } });
}
}
}
async handleUserDelete(data: IUserDeletionJob) {
const { user } = data;
// just for extra protection here
if (!this.isReadyForDeletion(user)) {
this.logger.warn(`Skipped user that was not ready for deletion: id=${user.id}`);
return;
}
this.logger.log(`Deleting user: ${user.id}`);
try {
const userAssetDir = join(APP_UPLOAD_LOCATION, user.id);
this.logger.warn(`Removing user from filesystem: ${userAssetDir}`);
await this.storageRepository.unlinkDir(userAssetDir, { recursive: true, force: true });
this.logger.warn(`Removing user from database: ${user.id}`);
await this.tokenRepository.deleteAll(user.id);
await this.keyRepository.deleteAll(user.id);
await this.albumRepository.deleteAll(user.id);
await this.assetRepository.deleteAll(user.id);
await this.userRepository.delete(user, true);
} catch (error: any) {
this.logger.error(`Failed to remove user`, error, { id: user.id });
}
}
private isReadyForDeletion(user: UserEntity): boolean {
if (!user.deletedAt) {
return false;
}
const msInDay = 86400000;
const msDeleteWait = msInDay * 7;
const msSinceDelete = new Date().getTime() - (Date.parse(user.deletedAt.toString()) || 0);
return msSinceDelete >= msDeleteWait;
}
}