324 lines
12 KiB
TypeScript
324 lines
12 KiB
TypeScript
import { FastifyInstance } from "fastify"
|
|
import { and, eq, ne } from "drizzle-orm"
|
|
import { authTenantUsers, authUsers } from "../../db/schema"
|
|
import { matrixService } from "../modules/matrix.service"
|
|
import { NotificationService, UserDirectory } from "../modules/notification.service"
|
|
|
|
const getUserDirectory: UserDirectory = async (server: FastifyInstance, userId) => {
|
|
const rows = await server.db
|
|
.select({ email: authUsers.email })
|
|
.from(authUsers)
|
|
.where(eq(authUsers.id, userId))
|
|
.limit(1)
|
|
|
|
return rows[0] || null
|
|
}
|
|
|
|
export default async function communicationRoutes(server: FastifyInstance) {
|
|
const matrix = matrixService(server)
|
|
const notifications = new NotificationService(server, getUserDirectory)
|
|
const handleMatrixError = (req: any, reply: any, err: any, fallbackMessage: string) => {
|
|
req.log.error(err)
|
|
return reply
|
|
.code(err.statusCode || 500)
|
|
.send({ error: err.message || fallbackMessage })
|
|
}
|
|
|
|
const roomOptionsFromRequest = (req: any) => {
|
|
const params = req.params as { roomKey?: string }
|
|
const body = (req.body || {}) as {
|
|
key?: string
|
|
name?: string
|
|
topic?: string
|
|
type?: string
|
|
entityType?: string | null
|
|
entityId?: number | null
|
|
entityUuid?: string | null
|
|
}
|
|
|
|
return {
|
|
key: params.roomKey || body.key,
|
|
name: body.name,
|
|
topic: body.topic,
|
|
type: body.type,
|
|
entityType: body.entityType,
|
|
entityId: body.entityId,
|
|
entityUuid: body.entityUuid,
|
|
}
|
|
}
|
|
|
|
const callModeFromRequest = (req: any): "audio" | "video" => {
|
|
const body = (req.body || {}) as { mode?: string }
|
|
return body.mode === "audio" ? "audio" : "video"
|
|
}
|
|
|
|
const notifyTenantUsersAboutCall = async (req: any, room: { key?: string; name?: string }, mode: "audio" | "video") => {
|
|
if (!req.user.tenant_id) return
|
|
|
|
try {
|
|
const recipientRows = await server.db
|
|
.select({ userId: authTenantUsers.user_id })
|
|
.from(authTenantUsers)
|
|
.where(and(
|
|
eq(authTenantUsers.tenant_id, req.user.tenant_id),
|
|
ne(authTenantUsers.user_id, req.user.user_id)
|
|
))
|
|
|
|
const userIds = recipientRows.map((row) => row.userId)
|
|
if (!userIds.length) return
|
|
|
|
await notifications.trigger({
|
|
tenantId: req.user.tenant_id,
|
|
userIds,
|
|
eventType: "communication.call.started",
|
|
title: mode === "audio" ? "Audioanruf gestartet" : "Videokonferenz gestartet",
|
|
message: `${room.name || room.key || "Ein Chatraum"} hat eine laufende Besprechung.`,
|
|
payload: {
|
|
link: "/communication/chat",
|
|
roomKey: room.key,
|
|
roomName: room.name,
|
|
mode,
|
|
},
|
|
channels: ["inapp", "push"],
|
|
})
|
|
} catch (err) {
|
|
req.log.error({ err }, "Call-Benachrichtigung konnte nicht ausgelöst werden")
|
|
}
|
|
}
|
|
|
|
server.get("/communication/matrix/status", async () => {
|
|
return matrix.getStatus()
|
|
})
|
|
|
|
server.get("/communication/matrix/me", async (req) => {
|
|
const userId = req.user.user_id
|
|
|
|
return {
|
|
matrixUserId: await matrix.matrixUserIdForUser(userId, req.user.tenant_id),
|
|
displayName: await matrix.getCurrentUserDisplayName(userId, req.user.tenant_id),
|
|
}
|
|
})
|
|
|
|
server.post("/communication/matrix/me/provision", async (req, reply) => {
|
|
try {
|
|
return await matrix.provisionCurrentUser(req.user.user_id, req.user.tenant_id)
|
|
} catch (err: any) {
|
|
return handleMatrixError(req, reply, err, "Matrix provisioning failed")
|
|
}
|
|
})
|
|
|
|
server.get("/communication/matrix/tenant-space", async (req, reply) => {
|
|
try {
|
|
return await matrix.getTenantSpaceStatus(req.user.tenant_id)
|
|
} catch (err: any) {
|
|
return handleMatrixError(req, reply, err, "Matrix tenant space status failed")
|
|
}
|
|
})
|
|
|
|
server.post("/communication/matrix/tenant-space/provision", async (req, reply) => {
|
|
try {
|
|
return await matrix.provisionCurrentTenantSpace(req.user.user_id, req.user.tenant_id)
|
|
} catch (err: any) {
|
|
return handleMatrixError(req, reply, err, "Matrix tenant space provisioning failed")
|
|
}
|
|
})
|
|
|
|
server.get("/communication/matrix/rooms", async (req, reply) => {
|
|
try {
|
|
return await matrix.listTenantRooms(req.user.tenant_id)
|
|
} catch (err: any) {
|
|
return handleMatrixError(req, reply, err, "Matrix rooms failed")
|
|
}
|
|
})
|
|
|
|
server.post("/communication/matrix/rooms", async (req, reply) => {
|
|
try {
|
|
return await matrix.provisionTenantRoom(
|
|
req.user.user_id,
|
|
req.user.tenant_id,
|
|
roomOptionsFromRequest(req)
|
|
)
|
|
} catch (err: any) {
|
|
return handleMatrixError(req, reply, err, "Matrix room provisioning failed")
|
|
}
|
|
})
|
|
|
|
server.get("/communication/matrix/rooms/general", async (req, reply) => {
|
|
try {
|
|
return await matrix.getTenantRoomStatus(req.user.tenant_id, "allgemein", "Allgemeiner Chat")
|
|
} catch (err: any) {
|
|
return handleMatrixError(req, reply, err, "Matrix room status failed")
|
|
}
|
|
})
|
|
|
|
server.post("/communication/matrix/rooms/general/provision", async (req, reply) => {
|
|
try {
|
|
return await matrix.provisionTenantRoom(req.user.user_id, req.user.tenant_id, {
|
|
key: "allgemein",
|
|
name: "Allgemeiner Chat",
|
|
})
|
|
} catch (err: any) {
|
|
return handleMatrixError(req, reply, err, "Matrix room provisioning failed")
|
|
}
|
|
})
|
|
|
|
server.get("/communication/matrix/rooms/general/messages", async (req, reply) => {
|
|
try {
|
|
return await matrix.getGeneralRoomMessages(req.user.user_id, req.user.tenant_id)
|
|
} catch (err: any) {
|
|
return handleMatrixError(req, reply, err, "Matrix messages failed")
|
|
}
|
|
})
|
|
|
|
server.get("/communication/matrix/rooms/general/members", async (req, reply) => {
|
|
try {
|
|
return await matrix.getGeneralRoomMembers(req.user.user_id, req.user.tenant_id)
|
|
} catch (err: any) {
|
|
return handleMatrixError(req, reply, err, "Matrix members failed")
|
|
}
|
|
})
|
|
|
|
server.post("/communication/matrix/rooms/general/session", async (req, reply) => {
|
|
try {
|
|
return await matrix.createElementRoomSession(req.user.user_id, req.user.tenant_id, {
|
|
key: "allgemein",
|
|
name: "Allgemeiner Chat",
|
|
})
|
|
} catch (err: any) {
|
|
return handleMatrixError(req, reply, err, "Matrix session failed")
|
|
}
|
|
})
|
|
|
|
server.post("/communication/matrix/rooms/general/call-session", async (req, reply) => {
|
|
try {
|
|
const room = {
|
|
key: "allgemein",
|
|
name: "Allgemeiner Chat",
|
|
}
|
|
const session = await matrix.createLiveKitRoomSession(req.user.user_id, req.user.tenant_id, room)
|
|
await notifyTenantUsersAboutCall(req, room, callModeFromRequest(req))
|
|
return session
|
|
} catch (err: any) {
|
|
return handleMatrixError(req, reply, err, "Matrix call session failed")
|
|
}
|
|
})
|
|
|
|
server.post("/communication/matrix/rooms/general/members/sync", async (req, reply) => {
|
|
try {
|
|
return await matrix.syncTenantRoomMembers(req.user.user_id, req.user.tenant_id, {
|
|
key: "allgemein",
|
|
name: "Allgemeiner Chat",
|
|
})
|
|
} catch (err: any) {
|
|
return handleMatrixError(req, reply, err, "Matrix member sync failed")
|
|
}
|
|
})
|
|
|
|
server.post("/communication/matrix/rooms/general/messages", async (req, reply) => {
|
|
try {
|
|
const body = req.body as { text?: string }
|
|
return await matrix.sendGeneralRoomMessage(req.user.user_id, req.user.tenant_id, body.text || "")
|
|
} catch (err: any) {
|
|
return handleMatrixError(req, reply, err, "Matrix message send failed")
|
|
}
|
|
})
|
|
|
|
server.get("/communication/matrix/rooms/:roomKey", async (req, reply) => {
|
|
try {
|
|
const params = req.params as { roomKey: string }
|
|
return await matrix.getTenantRoomStatus(req.user.tenant_id, params.roomKey)
|
|
} catch (err: any) {
|
|
return handleMatrixError(req, reply, err, "Matrix room status failed")
|
|
}
|
|
})
|
|
|
|
server.post("/communication/matrix/rooms/:roomKey/provision", async (req, reply) => {
|
|
try {
|
|
return await matrix.provisionTenantRoom(
|
|
req.user.user_id,
|
|
req.user.tenant_id,
|
|
roomOptionsFromRequest(req)
|
|
)
|
|
} catch (err: any) {
|
|
return handleMatrixError(req, reply, err, "Matrix room provisioning failed")
|
|
}
|
|
})
|
|
|
|
server.get("/communication/matrix/rooms/:roomKey/messages", async (req, reply) => {
|
|
try {
|
|
return await matrix.getTenantRoomMessages(
|
|
req.user.user_id,
|
|
req.user.tenant_id,
|
|
roomOptionsFromRequest(req)
|
|
)
|
|
} catch (err: any) {
|
|
return handleMatrixError(req, reply, err, "Matrix messages failed")
|
|
}
|
|
})
|
|
|
|
server.get("/communication/matrix/rooms/:roomKey/members", async (req, reply) => {
|
|
try {
|
|
return await matrix.getTenantRoomMembers(
|
|
req.user.user_id,
|
|
req.user.tenant_id,
|
|
roomOptionsFromRequest(req)
|
|
)
|
|
} catch (err: any) {
|
|
return handleMatrixError(req, reply, err, "Matrix members failed")
|
|
}
|
|
})
|
|
|
|
server.post("/communication/matrix/rooms/:roomKey/session", async (req, reply) => {
|
|
try {
|
|
return await matrix.createElementRoomSession(
|
|
req.user.user_id,
|
|
req.user.tenant_id,
|
|
roomOptionsFromRequest(req)
|
|
)
|
|
} catch (err: any) {
|
|
return handleMatrixError(req, reply, err, "Matrix session failed")
|
|
}
|
|
})
|
|
|
|
server.post("/communication/matrix/rooms/:roomKey/call-session", async (req, reply) => {
|
|
try {
|
|
const room = roomOptionsFromRequest(req)
|
|
const session = await matrix.createLiveKitRoomSession(
|
|
req.user.user_id,
|
|
req.user.tenant_id,
|
|
room
|
|
)
|
|
await notifyTenantUsersAboutCall(req, room, callModeFromRequest(req))
|
|
return session
|
|
} catch (err: any) {
|
|
return handleMatrixError(req, reply, err, "Matrix call session failed")
|
|
}
|
|
})
|
|
|
|
server.post("/communication/matrix/rooms/:roomKey/members/sync", async (req, reply) => {
|
|
try {
|
|
return await matrix.syncTenantRoomMembers(
|
|
req.user.user_id,
|
|
req.user.tenant_id,
|
|
roomOptionsFromRequest(req)
|
|
)
|
|
} catch (err: any) {
|
|
return handleMatrixError(req, reply, err, "Matrix member sync failed")
|
|
}
|
|
})
|
|
|
|
server.post("/communication/matrix/rooms/:roomKey/messages", async (req, reply) => {
|
|
try {
|
|
const body = req.body as { text?: string }
|
|
return await matrix.sendTenantRoomMessage(
|
|
req.user.user_id,
|
|
req.user.tenant_id,
|
|
roomOptionsFromRequest(req),
|
|
body.text || ""
|
|
)
|
|
} catch (err: any) {
|
|
return handleMatrixError(req, reply, err, "Matrix message send failed")
|
|
}
|
|
})
|
|
}
|