워크스페이스 디테일에 activity 내용을 출력해주기 위해
사용 로그를 만들기로 결정하였음
// audit-log.entity.ts
import { Column, CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
import { User } from './user.entitiy';
import { Workspace } from './workspace.entity';
import ActionType from '../utils/action-type';
@Entity('audit_logs')
export class Audit_log {
@PrimaryGeneratedColumn({ type: 'bigint' })
id: number;
@Column({ type: 'enum', enum: ActionType, nullable: false })
actions: ActionType;
@Column({ nullable: false })
details: string;
@CreateDateColumn()
created_at: Date;
@ManyToOne(() => User, (user) => user.audit_logs)
user: User;
@ManyToOne(() => Workspace, (workspace) => workspace.audit_logs, {
onDelete: 'CASCADE',
nullable: false,
})
workspace: Workspace;
}
초반에는 board, boardColumn, card도 관계설정을 해뒀다. 어떤 부분에서 이벤트가 발생하는지 출력을 해주기 위함이였는데 다시 생각해보니 워크스페이스 디테일에 들어가는 로그기 때문에, 어떤 유저와 어떤 워크스페이스에서 이벤트가 발생했는지만 출력해주면 될 것 같다고 판단되어 변경하였음
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Audit_log } from 'src/_common/entities/audit-log.entity';
import ActionType from 'src/_common/utils/action-type';
import { Repository } from 'typeorm';
@Injectable()
export class AuditLogsService {
constructor(
@InjectRepository(Audit_log)
private auditLogRepository: Repository<Audit_log>
) {}
async inviteMemberLog(
workspaceId: number,
inviterUserId: number,
inviterUserName: string,
invitedUserName: string
): Promise<Audit_log> {
const newLog = this.auditLogRepository.create({
workspace: { id: workspaceId },
user: { id: inviterUserId },
actions: ActionType.INVITE,
details: `멤버 초대 - ${inviterUserName}님이 ${invitedUserName}님을 워크스페이스에 초대하였습니다. `,
});
return await this.auditLogRepository.save(newLog);
}
async deleteMemberLog(
workspaceId: number,
userId: number,
userName: string,
deletedUser: string
): Promise<Audit_log> {
const newLog = this.auditLogRepository.create({
workspace: { id: workspaceId },
user: { id: userId },
actions: ActionType.DELETE,
details: `멤버 삭제 - ${userName}님이 ${deletedUser}님을 워크스페이스에서 내보냈습니다.`,
});
return await this.auditLogRepository.save(newLog);
}
async roleChangeLog(
workspaceId: number,
userId: number,
userName: string,
targetUser: string,
role: string
): Promise<Audit_log> {
const newLog = this.auditLogRepository.create({
workspace: { id: workspaceId },
user: { id: userId },
actions: ActionType.ROLE_CHANGE,
details: `역할 변경 - ${userName}님이 ${targetUser}님의 역할을 ${role}로 변경했습니다.`,
});
return await this.auditLogRepository.save(newLog);
}
async createBoardLog(workspaceId: number, boardName: string, userId: number, userName: string): Promise<Audit_log> {
const newLog = this.auditLogRepository.create({
workspace: { id: workspaceId },
user: { id: userId },
actions: ActionType.CREATE,
details: `보드 생성 - ${userName}님이 ${boardName} 보드를 생성하였습니다.`,
});
return await this.auditLogRepository.save(newLog);
}
async updateBoardLog(
workspaceId: number,
beforeBoardName: string,
afterBoardName: string,
userId: number,
userName: string
): Promise<Audit_log> {
if (beforeBoardName === afterBoardName) {
const newLog = this.auditLogRepository.create({
workspace: { id: workspaceId },
user: { id: userId },
actions: ActionType.UPDATE,
details: `보드 수정 - ${userName}님이 ${beforeBoardName} 보드를 수정하였습니다.`,
});
return await this.auditLogRepository.save(newLog);
} else {
const newLog = this.auditLogRepository.create({
workspace: { id: workspaceId },
user: { id: userId },
actions: ActionType.UPDATE,
details: `보드 수정 - ${userName}님이 ${beforeBoardName} 보드를 ${afterBoardName} 보드로 수정하였습니다.`,
});
return await this.auditLogRepository.save(newLog);
}
}
async deleteBoardLog(workspaceId: number, boardName: string, userId: number, userName: string): Promise<Audit_log> {
const newLog = this.auditLogRepository.create({
workspace: { id: workspaceId },
user: { id: userId },
actions: ActionType.DELETE,
details: `보드 삭제 - ${userName}님이 ${boardName} 보드를 삭제하였습니다.`,
});
return await this.auditLogRepository.save(newLog);
}
async createColumnLog(workspaceId: number, columnName: string, userId: number, userName: string): Promise<Audit_log> {
const newLog = this.auditLogRepository.create({
workspace: { id: workspaceId },
user: { id: userId },
actions: ActionType.CREATE,
details: `컬럼 생성 - ${userName}님이 ${columnName} 컬럼을 생성하였습니다.`,
});
return await this.auditLogRepository.save(newLog);
}
async updateColumnLog(
workspaceId: number,
beforeColumnName: string,
afterColumnName: string,
userId: number,
userName: string
): Promise<Audit_log> {
if (beforeColumnName === afterColumnName) {
const newLog = this.auditLogRepository.create({
workspace: { id: workspaceId },
user: { id: userId },
actions: ActionType.UPDATE,
details: `컬럼 수정 - ${userName}님이 ${beforeColumnName} 컬럼을 수정하였습니다.`,
});
return await this.auditLogRepository.save(newLog);
} else {
const newLog = this.auditLogRepository.create({
workspace: { id: workspaceId },
user: { id: userId },
actions: ActionType.UPDATE,
details: `컬럼 수정 - ${userName}님이 ${beforeColumnName} 컬럼을 ${afterColumnName} 컬럼으로 수정하였습니다.`,
});
return await this.auditLogRepository.save(newLog);
}
}
async deleteColumnLog(workspaceId: number, columnName: string, userId: number, userName: string): Promise<Audit_log> {
const newLog = this.auditLogRepository.create({
workspace: { id: workspaceId },
user: { id: userId },
actions: ActionType.DELETE,
details: `컬럼 삭제 - ${userName}님이 ${columnName} 컬럼을 삭제하였습니다.`,
});
return await this.auditLogRepository.save(newLog);
}
async createCardLog(workspaceId: number, cardName: string, userId: number, userName: string): Promise<Audit_log> {
const newLog = this.auditLogRepository.create({
workspace: { id: workspaceId },
user: { id: userId },
actions: ActionType.CREATE,
details: `카드 생성 - ${userName}님이 ${cardName} 카드를 생성하였습니다.`,
});
return await this.auditLogRepository.save(newLog);
}
async updateCardLog(
workspaceId: number,
beforeCardName: string,
afterCardName: string,
userId: number,
userName: string
) {
if (beforeCardName === afterCardName) {
const newLog = this.auditLogRepository.create({
workspace: { id: workspaceId },
user: { id: userId },
actions: ActionType.UPDATE,
details: `카드 수정 - ${userName}님이 ${beforeCardName} 카드를 수정하였습니다.`,
});
return await this.auditLogRepository.save(newLog);
} else {
const newLog = this.auditLogRepository.create({
workspace: { id: workspaceId },
user: { id: userId },
actions: ActionType.UPDATE,
details: `카드 수정 - ${userName}님이 ${beforeCardName} 카드를 ${afterCardName} 카드로 수정하였습니다.`,
});
return await this.auditLogRepository.save(newLog);
}
}
async deleteCardLog(workspaceId: number, cardName: string, userId: number, userName: string) {
const newLog = this.auditLogRepository.create({
workspace: { id: workspaceId },
user: { id: userId },
actions: ActionType.DELETE,
details: `카드 수정 - ${userName}님이 ${cardName} 카드를 삭제하였습니다.`,
});
return await this.auditLogRepository.save(newLog);
}
}
다소 반복되는감이 없지 않아 있긴 하지만 각 이벤트별로 출력될 로그들을 미리 작성해두고 테스트
//workspace.service.ts
// 멤버 권한 변경
async setMemberRole(
body: SetRoleDto,
workspaceId: number,
userId: number,
loginUserName: string,
loginUserId: number
): Promise<IResult> {
const existMember = await this.workspaceMemberRepository.findOne({
where: { workspace: { id: workspaceId }, user: { id: userId } },
relations: ['user'],
});
const loginUserRole = await this.loginUserRole(loginUserId, workspaceId);
if (!existMember) throw new HttpException('해당 멤버가 존재하지 않습니다.', HttpStatus.NOT_FOUND);
if (loginUserRole / 1 >= existMember.role)
throw new HttpException('관리자 또는 어드민 계정은 역할 변경이 불가합니다.', HttpStatus.BAD_REQUEST);
await this.workspaceMemberRepository.update(
{ user: { id: userId }, workspace: { id: workspaceId } },
{ role: body.role }
);
let role = '';
if (body.role === 2) role = 'Manager';
if (body.role === 3) role = 'Member';
if (body.role === 4) role = 'OutSourcing';
await this.auditLogService.roleChangeLog(workspaceId, userId, loginUserName, existMember.user.name, role);
return { result: true };
}
멤버권한 변경하는 코드의 하단부에 roleChangeLog 메서드를 추가해준 뒤 실행해보면
2번 유저가 15번 워크스페이스에서 만든 이벤트가 audit_log 테이블에 잘 쌓이고 있는 것을 확인.
이것을 이제 프론트에 연결해주는 작업 및 socket도 연결하여 실시간으로 이벤트가 발생할때마다 접속해있는 인원도 확인할 수 있게 만들면 마무리 될 것 같음!