Files
huly-platform/packages/server/src/eventProcessor.ts
T
2025-03-03 19:45:10 +04:00

429 lines
13 KiB
TypeScript

//
// Copyright © 2025 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
import {
type Message,
type Patch,
type Reaction,
type Attachment,
type SocialID,
type WorkspaceID,
PatchType
} from '@hcengineering/communication-types'
import {
type CreateAttachmentEvent,
type AttachmentCreatedEvent,
type CreateMessageEvent,
type MessageCreatedEvent,
type CreatePatchEvent,
type PatchCreatedEvent,
type CreateReactionEvent,
type ReactionCreatedEvent,
type RemoveAttachmentEvent,
type AttachmentRemovedEvent,
type RemoveMessageEvent,
type MessageRemovedEvent,
type RemoveReactionEvent,
type ReactionRemovedEvent,
type EventResult,
type DbAdapter,
type CreateNotificationEvent,
type RemoveNotificationEvent,
type CreateNotificationContextEvent,
type RemoveNotificationContextEvent,
type UpdateNotificationContextEvent,
type NotificationRemovedEvent,
type NotificationContextCreatedEvent,
type NotificationContextRemovedEvent,
type NotificationContextUpdatedEvent,
type ResponseEvent,
RequestEventType,
type RequestEvent,
ResponseEventType,
type CreateMessagesGroupEvent,
type RemoveMessagesEvent,
type ThreadCreatedEvent,
type CreateThreadEvent,
type ConnectionInfo,
type RemovePatchesEvent,
type RemoveMessagesGroupEvent,
type MessagesGroupCreatedEvent
} from '@hcengineering/communication-sdk-types'
import { systemAccountUuid, type Account } from '@hcengineering/core'
export interface Result {
responseEvent?: ResponseEvent
result: EventResult
}
export class EventProcessor {
constructor(
private readonly db: DbAdapter,
private readonly workspace: WorkspaceID
) {}
async process(info: ConnectionInfo, event: RequestEvent): Promise<Result> {
switch (event.type) {
case RequestEventType.CreateMessage:
return await this.createMessage(event, info)
case RequestEventType.RemoveMessage:
return await this.removeMessage(event, info)
case RequestEventType.RemoveMessages:
return await this.removeMessages(event, info)
case RequestEventType.CreatePatch:
return await this.createPatch(event, info)
case RequestEventType.CreateReaction:
return await this.createReaction(event, info)
case RequestEventType.RemoveReaction:
return await this.removeReaction(event, info)
case RequestEventType.CreateAttachment:
return await this.createAttachment(event, info)
case RequestEventType.RemoveAttachment:
return await this.removeAttachment(event, info)
case RequestEventType.CreateNotification:
return await this.createNotification(event, info)
case RequestEventType.RemoveNotification:
return await this.removeNotification(event, info)
case RequestEventType.CreateNotificationContext:
return await this.createNotificationContext(event, info)
case RequestEventType.RemoveNotificationContext:
return await this.removeNotificationContext(event, info)
case RequestEventType.UpdateNotificationContext:
return await this.updateNotificationContext(event, info)
case RequestEventType.CreateMessagesGroup:
return await this.createMessagesGroup(event, info)
case RequestEventType.CreateThread:
return await this.createThread(event, info)
case RequestEventType.RemovePatches:
return await this.removePatches(event, info)
case RequestEventType.RemoveMessagesGroup:
return await this.removeMessagesGroup(event, info)
}
}
private async createMessage(event: CreateMessageEvent, info: ConnectionInfo): Promise<Result> {
this.checkCreator(info.account, event.creator)
const created = new Date()
const id = await this.db.createMessage(event.card, event.content, event.creator, created)
const message: Message = {
id,
card: event.card,
content: event.content,
creator: event.creator,
created,
reactions: [],
attachments: []
}
const responseEvent: MessageCreatedEvent = {
type: ResponseEventType.MessageCreated,
message
}
return {
responseEvent,
result: { id }
}
}
private async createPatch(event: CreatePatchEvent, info: ConnectionInfo): Promise<Result> {
this.checkCreator(info.account, event.creator)
const created = new Date()
await this.db.createPatch(event.card, event.message, PatchType.update, event.content, event.creator, created)
const patch: Patch = {
type: PatchType.update,
message: event.message,
content: event.content,
creator: event.creator,
created
}
const responseEvent: PatchCreatedEvent = {
type: ResponseEventType.PatchCreated,
card: event.card,
patch
}
return {
responseEvent,
result: {}
}
}
private async removeMessage(event: RemoveMessageEvent, info: ConnectionInfo): Promise<Result> {
const socialIds = systemAccountUuid === info.account.uuid ? undefined : info.account.socialIds
await this.db.removeMessage(event.card, event.message, socialIds)
const responseEvent: MessageRemovedEvent = {
type: ResponseEventType.MessageRemoved,
card: event.card,
message: event.message
}
return {
responseEvent,
result: {}
}
}
private async removeMessages(event: RemoveMessagesEvent, info: ConnectionInfo): Promise<Result> {
if (systemAccountUuid !== info.account.uuid) {
throw new Error('Forbidden')
}
await this.db.removeMessages(event.card, event.fromId, event.toId)
return {
result: {}
}
}
private async removePatches(event: RemovePatchesEvent, info: ConnectionInfo): Promise<Result> {
if (systemAccountUuid !== info.account.uuid) {
throw new Error('Forbidden')
}
await this.db.removePatches(event.card, event.fromId, event.toId)
return {
result: {}
}
}
private async createReaction(event: CreateReactionEvent, info: ConnectionInfo): Promise<Result> {
this.checkCreator(info.account, event.creator)
const created = new Date()
await this.db.createReaction(event.card, event.message, event.reaction, event.creator, created)
const reaction: Reaction = {
message: event.message,
reaction: event.reaction,
creator: event.creator,
created
}
const responseEvent: ReactionCreatedEvent = {
type: ResponseEventType.ReactionCreated,
card: event.card,
reaction
}
return {
responseEvent,
result: {}
}
}
private async removeReaction(event: RemoveReactionEvent, info: ConnectionInfo): Promise<Result> {
this.checkCreator(info.account, event.creator)
await this.db.removeReaction(event.card, event.message, event.reaction, event.creator)
const responseEvent: ReactionRemovedEvent = {
type: ResponseEventType.ReactionRemoved,
card: event.card,
message: event.message,
reaction: event.reaction,
creator: event.creator
}
return {
responseEvent,
result: {}
}
}
private async createAttachment(event: CreateAttachmentEvent, info: ConnectionInfo): Promise<Result> {
this.checkCreator(info.account, event.creator)
const created = new Date()
await this.db.createAttachment(event.message, event.card, event.creator, created)
const attachment: Attachment = {
message: event.message,
card: event.card,
creator: event.creator,
created
}
const responseEvent: AttachmentCreatedEvent = {
type: ResponseEventType.AttachmentCreated,
card: event.card,
attachment
}
return {
responseEvent,
result: {}
}
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
private async removeAttachment(event: RemoveAttachmentEvent, _: ConnectionInfo): Promise<Result> {
await this.db.removeAttachment(event.message, event.card)
const responseEvent: AttachmentRemovedEvent = {
type: ResponseEventType.AttachmentRemoved,
card: event.card,
message: event.message,
attachment: event.attachment
}
return {
responseEvent,
result: {}
}
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
private async createNotification(event: CreateNotificationEvent, _: ConnectionInfo): Promise<Result> {
await this.db.createNotification(event.message, event.context)
return {
result: {}
}
}
//eslint-disable-next-line @typescript-eslint/no-unused-vars
private async removeNotification(event: RemoveNotificationEvent, info: ConnectionInfo): Promise<Result> {
await this.db.removeNotification(event.message, event.context)
const responseEvent: NotificationRemovedEvent = {
type: ResponseEventType.NotificationRemoved,
// personalWorkspace: info.personalWorkspace,
// TODO: add personal workspace
personalWorkspace: '' as WorkspaceID,
message: event.message,
context: event.context
}
return {
responseEvent,
result: {}
}
}
private async createNotificationContext(
event: CreateNotificationContextEvent,
//eslint-disable-next-line @typescript-eslint/no-unused-vars
info: ConnectionInfo
): Promise<Result> {
// TODO: add personal workspace
const personalWorkspace = '' as WorkspaceID
const id = await this.db.createContext(personalWorkspace, event.card, event.lastView, event.lastUpdate)
const responseEvent: NotificationContextCreatedEvent = {
type: ResponseEventType.NotificationContextCreated,
context: {
id,
workspace: this.workspace,
personalWorkspace,
card: event.card,
lastView: event.lastView,
lastUpdate: event.lastUpdate
}
}
return {
responseEvent,
result: { id }
}
}
private async removeNotificationContext(
event: RemoveNotificationContextEvent,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
info: ConnectionInfo
): Promise<Result> {
await this.db.removeContext(event.context)
const responseEvent: NotificationContextRemovedEvent = {
type: ResponseEventType.NotificationContextRemoved,
// personalWorkspace: info.personalWorkspace,
// TODO: add personal workspace
personalWorkspace: '' as WorkspaceID,
context: event.context
}
return {
responseEvent,
result: {}
}
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async updateNotificationContext(event: UpdateNotificationContextEvent, info: ConnectionInfo): Promise<Result> {
await this.db.updateContext(event.context, event.update)
const responseEvent: NotificationContextUpdatedEvent = {
type: ResponseEventType.NotificationContextUpdated,
// personalWorkspace: info.personalWorkspace,
// TODO: add personal workspace
personalWorkspace: '' as WorkspaceID,
context: event.context,
update: event.update
}
return {
responseEvent,
result: {}
}
}
async createMessagesGroup(event: CreateMessagesGroupEvent, info: ConnectionInfo): Promise<Result> {
if (systemAccountUuid !== info.account.uuid) {
throw new Error('Forbidden')
}
const { fromDate, toDate, count, fromId, toId, card, blobId } = event.group
await this.db.createMessagesGroup(card, blobId, fromDate, toDate, fromId, toId, count)
const responseEvent: MessagesGroupCreatedEvent = {
type: ResponseEventType.MessagesGroupCreated,
group: {
card,
blobId,
fromDate,
toDate,
fromId,
toId,
count
}
}
return {
responseEvent,
result: {}
}
}
async removeMessagesGroup(event: RemoveMessagesGroupEvent, info: ConnectionInfo): Promise<Result> {
if (systemAccountUuid !== info.account.uuid) {
throw new Error('Forbidden')
}
await this.db.removeMessagesGroup(event.card, event.blobId)
return {
responseEvent: undefined,
result: {}
}
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
private async createThread(event: CreateThreadEvent, _: ConnectionInfo): Promise<Result> {
const date = new Date()
await this.db.createThread(event.card, event.message, event.thread, date)
const responseEvent: ThreadCreatedEvent = {
type: ResponseEventType.ThreadCreated,
thread: {
card: event.card,
thread: event.thread,
message: event.message,
repliesCount: 0,
lastReply: date
}
}
return {
responseEvent,
result: {}
}
}
private checkCreator(account: Account, creator: SocialID): void {
if (!account.socialIds.includes(creator) && systemAccountUuid !== account.uuid) {
throw new Error('Forbidden')
}
}
}