import { Container } from 'typedi'; import type { EntitySubscriberInterface, UpdateEvent } from '@n8n/typeorm'; import { EventSubscriber } from '@n8n/typeorm'; import { ApplicationError, ErrorReporterProxy } from 'n8n-workflow'; import { Logger } from '@/logger'; import { Project } from '../entities/Project'; import { User } from '../entities/User'; import { UserRepository } from '../repositories/user.repository'; @EventSubscriber() export class UserSubscriber implements EntitySubscriberInterface { listenTo() { return User; } async afterUpdate(event: UpdateEvent): Promise { if (event.entity) { const newUserData = event.entity; if (event.databaseEntity) { const fields = event.updatedColumns.map((c) => c.propertyName); if ( fields.includes('firstName') || fields.includes('lastName') || fields.includes('email') ) { const oldUser = event.databaseEntity; const userEntity = newUserData instanceof User ? newUserData : Container.get(UserRepository).create(newUserData); const projectName = userEntity.createPersonalProjectName(); const project = await event.manager.findOneBy(Project, { type: 'personal', projectRelations: { userId: oldUser.id }, }); if (!project) { // Since this is benign we're not throwing the exception. We don't // know if we're running inside a transaction and thus there is a risk // that this could cause further data inconsistencies. const message = "Could not update the personal project's name"; Container.get(Logger).warn(message, event.entity); const exception = new ApplicationError(message); ErrorReporterProxy.warn(exception, event.entity); return; } project.name = projectName; await event.manager.save(Project, project); } } else { // This means the user was updated using `Repository.update`. In this // case we're missing the user's id and cannot update their project. // // When updating the user's firstName, lastName or email we must use // `Repository.save`, so this is a bug and we should report it to sentry. // if (event.entity.firstName || event.entity.lastName || event.entity.email) { // Since this is benign we're not throwing the exception. We don't // know if we're running inside a transaction and thus there is a risk // that this could cause further data inconsistencies. const message = "Could not update the personal project's name"; Container.get(Logger).warn(message, event.entity); const exception = new ApplicationError(message); ErrorReporterProxy.warn(exception, event.entity); } } } } }