refactor: user delete (#23163)

This commit is contained in:
Jason Rasmussen 2025-10-22 12:54:29 -04:00 committed by GitHub
parent 8c27ba3e52
commit 834e52fda6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 15 additions and 12 deletions

View File

@ -94,6 +94,9 @@ type EventMap = {
// user events // user events
UserSignup: [{ notify: boolean; id: string; password?: string }]; UserSignup: [{ notify: boolean; id: string; password?: string }];
UserCreate: [UserEvent]; UserCreate: [UserEvent];
/** user is soft deleted */
UserTrash: [UserEvent];
/** user is permanently deleted */
UserDelete: [UserEvent]; UserDelete: [UserEvent];
UserRestore: [UserEvent]; UserRestore: [UserEvent];

View File

@ -421,11 +421,6 @@ export class JobService extends BaseService {
} }
break; break;
} }
case JobName.UserDelete: {
this.eventRepository.clientBroadcast('on_user_delete', item.data.id);
break;
}
} }
} }
} }

View File

@ -202,6 +202,11 @@ export class NotificationService extends BaseService {
} }
} }
@OnEvent({ name: 'UserDelete' })
onUserDelete({ id }: ArgOf<'UserDelete'>) {
this.eventRepository.clientBroadcast('on_user_delete', id);
}
@OnEvent({ name: 'AlbumUpdate' }) @OnEvent({ name: 'AlbumUpdate' })
async onAlbumUpdate({ id, recipientId }: ArgOf<'AlbumUpdate'>) { async onAlbumUpdate({ id, recipientId }: ArgOf<'AlbumUpdate'>) {
await this.jobRepository.removeJob(JobName.NotifyAlbumUpdate, `${id}/${recipientId}`); await this.jobRepository.removeJob(JobName.NotifyAlbumUpdate, `${id}/${recipientId}`);

View File

@ -16,8 +16,8 @@ export class TelemetryService extends BaseService {
this.telemetryRepository.api.addToGauge(`immich.users.total`, 1); this.telemetryRepository.api.addToGauge(`immich.users.total`, 1);
} }
@OnEvent({ name: 'UserDelete' }) @OnEvent({ name: 'UserTrash' })
onUserDelete() { onUserTrash() {
this.telemetryRepository.api.addToGauge(`immich.users.total`, -1); this.telemetryRepository.api.addToGauge(`immich.users.total`, -1);
} }

View File

@ -104,7 +104,7 @@ export class UserAdminService extends BaseService {
const status = force ? UserStatus.Removing : UserStatus.Deleted; const status = force ? UserStatus.Removing : UserStatus.Deleted;
const user = await this.userRepository.update(id, { status, deletedAt: new Date() }); const user = await this.userRepository.update(id, { status, deletedAt: new Date() });
await this.eventRepository.emit('UserDelete', user); await this.eventRepository.emit('UserTrash', user);
if (force) { if (force) {
await this.jobRepository.queue({ name: JobName.UserDelete, data: { id: user.id, force } }); await this.jobRepository.queue({ name: JobName.UserDelete, data: { id: user.id, force } });

View File

@ -228,17 +228,17 @@ export class UserService extends BaseService {
} }
@OnJob({ name: JobName.UserDelete, queue: QueueName.BackgroundTask }) @OnJob({ name: JobName.UserDelete, queue: QueueName.BackgroundTask })
async handleUserDelete({ id, force }: JobOf<JobName.UserDelete>): Promise<JobStatus> { async handleUserDelete({ id, force }: JobOf<JobName.UserDelete>) {
const config = await this.getConfig({ withCache: false }); const config = await this.getConfig({ withCache: false });
const user = await this.userRepository.get(id, { withDeleted: true }); const user = await this.userRepository.get(id, { withDeleted: true });
if (!user) { if (!user) {
return JobStatus.Failed; return;
} }
// just for extra protection here // just for extra protection here
if (!force && !this.isReadyForDeletion(user, config.user.deleteDelay)) { if (!force && !this.isReadyForDeletion(user, config.user.deleteDelay)) {
this.logger.warn(`Skipped user that was not ready for deletion: id=${id}`); this.logger.warn(`Skipped user that was not ready for deletion: id=${id}`);
return JobStatus.Skipped; return;
} }
this.logger.log(`Deleting user: ${user.id}`); this.logger.log(`Deleting user: ${user.id}`);
@ -260,7 +260,7 @@ export class UserService extends BaseService {
await this.albumRepository.deleteAll(user.id); await this.albumRepository.deleteAll(user.id);
await this.userRepository.delete(user, true); await this.userRepository.delete(user, true);
return JobStatus.Success; await this.eventRepository.emit('UserDelete', user);
} }
private isReadyForDeletion(user: { id: string; deletedAt?: Date | null }, deleteDelay: number): boolean { private isReadyForDeletion(user: { id: string; deletedAt?: Date | null }, deleteDelay: number): boolean {