mirror of
https://github.com/docmost/docmost.git
synced 2026-05-18 23:44:24 +08:00
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
Inject,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
@@ -17,6 +18,11 @@ import { SpaceRole } from '../../../common/helpers/types/permission';
|
||||
import { CursorPaginationResult } from '@docmost/db/pagination/cursor-pagination';
|
||||
import { WatcherRepo } from '@docmost/db/repos/watcher/watcher.repo';
|
||||
import { executeTx } from '@docmost/db/utils';
|
||||
import { AuditEvent, AuditResource } from '../../../common/events/audit-events';
|
||||
import {
|
||||
AUDIT_SERVICE,
|
||||
IAuditService,
|
||||
} from '../../../integrations/audit/audit.service';
|
||||
|
||||
@Injectable()
|
||||
export class SpaceMemberService {
|
||||
@@ -26,6 +32,7 @@ export class SpaceMemberService {
|
||||
private spaceRepo: SpaceRepo,
|
||||
private watcherRepo: WatcherRepo,
|
||||
@InjectKysely() private readonly db: KyselyDB,
|
||||
@Inject(AUDIT_SERVICE) private readonly auditService: IAuditService,
|
||||
) {}
|
||||
|
||||
async addUserToSpace(
|
||||
@@ -90,7 +97,6 @@ export class SpaceMemberService {
|
||||
authUser: User,
|
||||
workspaceId: string,
|
||||
): Promise<void> {
|
||||
// await this.spaceService.findAndValidateSpace(spaceId, workspaceId);
|
||||
|
||||
const space = await this.spaceRepo.findById(dto.spaceId, workspaceId);
|
||||
if (!space) {
|
||||
@@ -164,8 +170,45 @@ export class SpaceMemberService {
|
||||
|
||||
if (membersToAdd.length > 0) {
|
||||
await this.spaceMemberRepo.insertSpaceMember(membersToAdd);
|
||||
} else {
|
||||
// either they are already members or do not exist on the workspace
|
||||
|
||||
// Audit log for each member added
|
||||
for (const user of validUsers) {
|
||||
this.auditService.log({
|
||||
event: AuditEvent.SPACE_MEMBER_ADDED,
|
||||
resourceType: AuditResource.SPACE_MEMBER,
|
||||
resourceId: dto.spaceId,
|
||||
spaceId: dto.spaceId,
|
||||
changes: {
|
||||
after: { role: dto.role },
|
||||
},
|
||||
metadata: {
|
||||
spaceId: dto.spaceId,
|
||||
spaceName: space.name,
|
||||
userId: user.id,
|
||||
userName: user.name,
|
||||
memberType: 'user',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
for (const group of validGroups) {
|
||||
this.auditService.log({
|
||||
event: AuditEvent.SPACE_MEMBER_ADDED,
|
||||
resourceType: AuditResource.SPACE_MEMBER,
|
||||
resourceId: dto.spaceId,
|
||||
spaceId: dto.spaceId,
|
||||
changes: {
|
||||
after: { role: dto.role },
|
||||
},
|
||||
metadata: {
|
||||
spaceId: dto.spaceId,
|
||||
spaceName: space.name,
|
||||
groupId: group.id,
|
||||
groupName: group.name,
|
||||
memberType: 'group',
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,6 +273,23 @@ export class SpaceMemberService {
|
||||
{ trx },
|
||||
);
|
||||
});
|
||||
|
||||
this.auditService.log({
|
||||
event: AuditEvent.SPACE_MEMBER_REMOVED,
|
||||
resourceType: AuditResource.SPACE_MEMBER,
|
||||
resourceId: dto.spaceId,
|
||||
spaceId: dto.spaceId,
|
||||
changes: {
|
||||
before: { role: spaceMember.role },
|
||||
},
|
||||
metadata: {
|
||||
spaceId: dto.spaceId,
|
||||
spaceName: space.name,
|
||||
userId: spaceMember.userId,
|
||||
groupId: spaceMember.groupId,
|
||||
memberType: spaceMember.userId ? 'user' : 'group',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async updateSpaceMemberRole(
|
||||
@@ -280,6 +340,24 @@ export class SpaceMemberService {
|
||||
spaceMember.id,
|
||||
dto.spaceId,
|
||||
);
|
||||
|
||||
this.auditService.log({
|
||||
event: AuditEvent.SPACE_MEMBER_ROLE_CHANGED,
|
||||
resourceType: AuditResource.SPACE_MEMBER,
|
||||
resourceId: dto.spaceId,
|
||||
spaceId: dto.spaceId,
|
||||
changes: {
|
||||
before: { role: spaceMember.role },
|
||||
after: { role: dto.role },
|
||||
},
|
||||
metadata: {
|
||||
spaceId: dto.spaceId,
|
||||
spaceName: space.name,
|
||||
userId: spaceMember.userId,
|
||||
groupId: spaceMember.groupId,
|
||||
memberType: spaceMember.userId ? 'user' : 'group',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async validateLastAdmin(spaceId: string): Promise<void> {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
ForbiddenException,
|
||||
Inject,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
@@ -21,6 +22,12 @@ import { CursorPaginationResult } from '@docmost/db/pagination/cursor-pagination
|
||||
import { ShareRepo } from '@docmost/db/repos/share/share.repo';
|
||||
import { WorkspaceRepo } from '@docmost/db/repos/workspace/workspace.repo';
|
||||
import { LicenseCheckService } from '../../../integrations/environment/license-check.service';
|
||||
import { AuditEvent, AuditResource } from '../../../common/events/audit-events';
|
||||
import { diffAuditTrackedFields } from '../../../common/helpers';
|
||||
import {
|
||||
AUDIT_SERVICE,
|
||||
IAuditService,
|
||||
} from '../../../integrations/audit/audit.service';
|
||||
|
||||
@Injectable()
|
||||
export class SpaceService {
|
||||
@@ -32,6 +39,7 @@ export class SpaceService {
|
||||
private licenseCheckService: LicenseCheckService,
|
||||
@InjectKysely() private readonly db: KyselyDB,
|
||||
@InjectQueue(QueueName.ATTACHMENT_QUEUE) private attachmentQueue: Queue,
|
||||
@Inject(AUDIT_SERVICE) private readonly auditService: IAuditService,
|
||||
) {}
|
||||
|
||||
async createSpace(
|
||||
@@ -63,6 +71,19 @@ export class SpaceService {
|
||||
trx,
|
||||
);
|
||||
|
||||
this.auditService.log({
|
||||
event: AuditEvent.SPACE_CREATED,
|
||||
resourceType: AuditResource.SPACE,
|
||||
resourceId: space.id,
|
||||
spaceId: space.id,
|
||||
changes: {
|
||||
after: {
|
||||
name: space.name,
|
||||
slug: space.slug,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return { ...space, memberCount: 1 };
|
||||
}
|
||||
|
||||
@@ -124,28 +145,74 @@ export class SpaceService {
|
||||
'This feature requires a valid enterprise license',
|
||||
);
|
||||
}
|
||||
|
||||
await this.spaceRepo.updateSharingSettings(
|
||||
updateSpaceDto.spaceId,
|
||||
workspaceId,
|
||||
'disabled',
|
||||
updateSpaceDto.disablePublicSharing,
|
||||
);
|
||||
|
||||
if (updateSpaceDto.disablePublicSharing) {
|
||||
await this.shareRepo.deleteBySpaceId(updateSpaceDto.spaceId);
|
||||
}
|
||||
}
|
||||
|
||||
return await this.spaceRepo.updateSpace(
|
||||
{
|
||||
name: updateSpaceDto.name,
|
||||
description: updateSpaceDto.description,
|
||||
slug: updateSpaceDto.slug,
|
||||
},
|
||||
const spaceBefore = await this.spaceRepo.findById(
|
||||
updateSpaceDto.spaceId,
|
||||
workspaceId,
|
||||
);
|
||||
const settingsBefore = (spaceBefore?.settings ?? {}) as Record<string, any>;
|
||||
|
||||
const before: Record<string, any> = {};
|
||||
const after: Record<string, any> = {};
|
||||
|
||||
let updatedSpace: Space;
|
||||
|
||||
await executeTx(this.db, async (trx) => {
|
||||
if (typeof updateSpaceDto.disablePublicSharing !== 'undefined') {
|
||||
const prev = settingsBefore?.sharing?.disabled ?? false;
|
||||
if (prev !== updateSpaceDto.disablePublicSharing) {
|
||||
before.disablePublicSharing = prev;
|
||||
after.disablePublicSharing = updateSpaceDto.disablePublicSharing;
|
||||
}
|
||||
|
||||
await this.spaceRepo.updateSharingSettings(
|
||||
updateSpaceDto.spaceId,
|
||||
workspaceId,
|
||||
'disabled',
|
||||
updateSpaceDto.disablePublicSharing,
|
||||
trx,
|
||||
);
|
||||
|
||||
if (updateSpaceDto.disablePublicSharing) {
|
||||
await this.shareRepo.deleteBySpaceId(updateSpaceDto.spaceId, trx);
|
||||
}
|
||||
}
|
||||
|
||||
updatedSpace = await this.spaceRepo.updateSpace(
|
||||
{
|
||||
name: updateSpaceDto.name,
|
||||
description: updateSpaceDto.description,
|
||||
slug: updateSpaceDto.slug,
|
||||
},
|
||||
updateSpaceDto.spaceId,
|
||||
workspaceId,
|
||||
trx,
|
||||
);
|
||||
});
|
||||
|
||||
const columnChanges = diffAuditTrackedFields(
|
||||
['name', 'slug', 'description'],
|
||||
updateSpaceDto,
|
||||
spaceBefore,
|
||||
updatedSpace,
|
||||
);
|
||||
if (columnChanges) {
|
||||
Object.assign(before, columnChanges.before);
|
||||
Object.assign(after, columnChanges.after);
|
||||
}
|
||||
|
||||
if (Object.keys(after).length > 0) {
|
||||
this.auditService.log({
|
||||
event: AuditEvent.SPACE_UPDATED,
|
||||
resourceType: AuditResource.SPACE,
|
||||
resourceId: updateSpaceDto.spaceId,
|
||||
spaceId: updateSpaceDto.spaceId,
|
||||
changes: { before, after },
|
||||
});
|
||||
}
|
||||
|
||||
return updatedSpace;
|
||||
}
|
||||
|
||||
async getSpaceInfo(spaceId: string, workspaceId: string): Promise<Space> {
|
||||
@@ -174,5 +241,19 @@ export class SpaceService {
|
||||
|
||||
await this.spaceRepo.deleteSpace(spaceId, workspaceId);
|
||||
await this.attachmentQueue.add(QueueJob.DELETE_SPACE_ATTACHMENTS, space);
|
||||
|
||||
this.auditService.log({
|
||||
event: AuditEvent.SPACE_DELETED,
|
||||
resourceType: AuditResource.SPACE,
|
||||
resourceId: spaceId,
|
||||
spaceId: spaceId,
|
||||
changes: {
|
||||
before: {
|
||||
name: space.name,
|
||||
slug: space.slug,
|
||||
description: space.description,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user