KI-AGENT: Matrix-Raum-API verallgemeinern
This commit is contained in:
@@ -35,6 +35,12 @@ type MatrixUserSession = {
|
||||
validUntilMs: number
|
||||
}
|
||||
|
||||
type MatrixTenantRoomOptions = {
|
||||
key?: string
|
||||
name?: string
|
||||
topic?: string
|
||||
}
|
||||
|
||||
type MatrixCachedValue<T = any> = {
|
||||
exists: true
|
||||
cachedUntil: number
|
||||
@@ -48,6 +54,13 @@ const matrixTenantSpaceCache = new Map<string, MatrixCachedValue>()
|
||||
const matrixTenantRoomCache = new Map<string, MatrixCachedValue>()
|
||||
let matrixServiceSessionCache: MatrixUserSession | null = null
|
||||
|
||||
const defaultTenantRooms: Required<Pick<MatrixTenantRoomOptions, "key" | "name">>[] = [
|
||||
{
|
||||
key: "allgemein",
|
||||
name: "Allgemeiner Chat",
|
||||
},
|
||||
]
|
||||
|
||||
const trimTrailingSlash = (value: string) => value.replace(/\/+$/, "")
|
||||
const readLocalDevRegistrationSharedSecret = () => {
|
||||
if (process.env.NODE_ENV === "production") return ""
|
||||
@@ -173,6 +186,19 @@ export function matrixService(server: FastifyInstance) {
|
||||
roomKey: string
|
||||
) => `#${tenantRoomAliasLocalpart(tenant, roomKey)}:${serverName()}`
|
||||
|
||||
const normalizeTenantRoomOptions = (options: MatrixTenantRoomOptions = {}) => {
|
||||
const fallbackName = options.key || options.name || "Allgemeiner Chat"
|
||||
const key = normalizeMatrixAliasSeed(options.key || fallbackName)
|
||||
const name = (options.name || fallbackName).trim() || "Allgemeiner Chat"
|
||||
const topic = options.topic?.trim()
|
||||
|
||||
return {
|
||||
key,
|
||||
name,
|
||||
topic,
|
||||
}
|
||||
}
|
||||
|
||||
const buildSharedSecretMac = (
|
||||
nonce: string,
|
||||
username: string,
|
||||
@@ -637,16 +663,13 @@ export function matrixService(server: FastifyInstance) {
|
||||
const provisionTenantRoom = async (
|
||||
userId: string,
|
||||
tenantId: number | null,
|
||||
options: {
|
||||
key?: string
|
||||
name?: string
|
||||
topic?: string
|
||||
} = {}
|
||||
options: MatrixTenantRoomOptions = {}
|
||||
) => {
|
||||
const tenant = await getCurrentTenant(tenantId)
|
||||
const key = normalizeMatrixAliasSeed(options.key || options.name || "allgemein")
|
||||
const name = (options.name || "Allgemeiner Chat").trim() || "Allgemeiner Chat"
|
||||
const topic = (options.topic || `Allgemeiner Kommunikationsraum für ${tenant.name}`).trim()
|
||||
const normalizedOptions = normalizeTenantRoomOptions(options)
|
||||
const key = normalizedOptions.key
|
||||
const name = normalizedOptions.name
|
||||
const topic = (normalizedOptions.topic || `Allgemeiner Kommunikationsraum für ${tenant.name}`).trim()
|
||||
const cacheKey = `${tenant.id}:${key}`
|
||||
const cachedRoom = matrixTenantRoomCache.get(cacheKey)
|
||||
|
||||
@@ -780,11 +803,39 @@ export function matrixService(server: FastifyInstance) {
|
||||
return session
|
||||
}
|
||||
|
||||
const getGeneralRoomMessages = async (userId: string, tenantId: number | null) => {
|
||||
const room = await provisionTenantRoom(userId, tenantId, {
|
||||
key: "allgemein",
|
||||
name: "Allgemeiner Chat",
|
||||
})
|
||||
const listTenantRooms = async (tenantId: number | null) => {
|
||||
const tenant = await getCurrentTenant(tenantId)
|
||||
const rooms = new Map<string, any>()
|
||||
|
||||
for (const room of defaultTenantRooms) {
|
||||
rooms.set(room.key, await getTenantRoomStatus(tenant.id, room.key, room.name))
|
||||
}
|
||||
|
||||
for (const [cacheKey, cachedRoom] of matrixTenantRoomCache.entries()) {
|
||||
const [cachedTenantId, roomKey] = cacheKey.split(":")
|
||||
if (cachedTenantId !== String(tenant.id) || !cachedRoom.value) continue
|
||||
|
||||
rooms.set(roomKey, {
|
||||
...cachedRoom.value,
|
||||
exists: true,
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
tenantId: tenant.id,
|
||||
tenantName: tenant.name,
|
||||
rooms: Array.from(rooms.values()).sort((a, b) =>
|
||||
String(a.name || a.key).localeCompare(String(b.name || b.key), "de")
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
const getTenantRoomMessages = async (
|
||||
userId: string,
|
||||
tenantId: number | null,
|
||||
options: MatrixTenantRoomOptions = {}
|
||||
) => {
|
||||
const room = await provisionTenantRoom(userId, tenantId, options)
|
||||
|
||||
const session = await ensureCurrentUserJoinedRoom(userId, tenantId, {
|
||||
roomId: room.roomId,
|
||||
@@ -807,6 +858,8 @@ export function matrixService(server: FastifyInstance) {
|
||||
return {
|
||||
roomId: room.roomId,
|
||||
alias: room.alias,
|
||||
key: room.key,
|
||||
name: room.name,
|
||||
matrixUserId: session.matrixUserId,
|
||||
messages: response.chunk
|
||||
.filter((event) => event.type === "m.room.message" && event.content?.msgtype === "m.text")
|
||||
@@ -822,11 +875,12 @@ export function matrixService(server: FastifyInstance) {
|
||||
}
|
||||
}
|
||||
|
||||
const getGeneralRoomMembers = async (userId: string, tenantId: number | null) => {
|
||||
const room = await provisionTenantRoom(userId, tenantId, {
|
||||
key: "allgemein",
|
||||
name: "Allgemeiner Chat",
|
||||
})
|
||||
const getTenantRoomMembers = async (
|
||||
userId: string,
|
||||
tenantId: number | null,
|
||||
options: MatrixTenantRoomOptions = {}
|
||||
) => {
|
||||
const room = await provisionTenantRoom(userId, tenantId, options)
|
||||
|
||||
const session = await ensureCurrentUserJoinedRoom(userId, tenantId, {
|
||||
roomId: room.roomId,
|
||||
@@ -840,6 +894,8 @@ export function matrixService(server: FastifyInstance) {
|
||||
return {
|
||||
roomId: room.roomId,
|
||||
alias: room.alias,
|
||||
key: room.key,
|
||||
name: room.name,
|
||||
members: Object.entries(members.joined).map(([matrixUserId, member]) => ({
|
||||
matrixUserId,
|
||||
displayName: member.display_name || matrixUserId,
|
||||
@@ -849,9 +905,10 @@ export function matrixService(server: FastifyInstance) {
|
||||
}
|
||||
}
|
||||
|
||||
const sendGeneralRoomMessage = async (
|
||||
const sendTenantRoomMessage = async (
|
||||
userId: string,
|
||||
tenantId: number | null,
|
||||
options: MatrixTenantRoomOptions = {},
|
||||
text: string
|
||||
) => {
|
||||
const message = text.trim()
|
||||
@@ -863,10 +920,7 @@ export function matrixService(server: FastifyInstance) {
|
||||
)
|
||||
}
|
||||
|
||||
const room = await provisionTenantRoom(userId, tenantId, {
|
||||
key: "allgemein",
|
||||
name: "Allgemeiner Chat",
|
||||
})
|
||||
const room = await provisionTenantRoom(userId, tenantId, options)
|
||||
|
||||
const session = await ensureCurrentUserJoinedRoom(userId, tenantId, {
|
||||
roomId: room.roomId,
|
||||
@@ -896,9 +950,33 @@ export function matrixService(server: FastifyInstance) {
|
||||
own: true,
|
||||
roomId: room.roomId,
|
||||
alias: room.alias,
|
||||
key: room.key,
|
||||
}
|
||||
}
|
||||
|
||||
const getGeneralRoomMessages = (userId: string, tenantId: number | null) =>
|
||||
getTenantRoomMessages(userId, tenantId, {
|
||||
key: "allgemein",
|
||||
name: "Allgemeiner Chat",
|
||||
})
|
||||
|
||||
const getGeneralRoomMembers = (userId: string, tenantId: number | null) =>
|
||||
getTenantRoomMembers(userId, tenantId, {
|
||||
key: "allgemein",
|
||||
name: "Allgemeiner Chat",
|
||||
})
|
||||
|
||||
const sendGeneralRoomMessage = (userId: string, tenantId: number | null, text: string) =>
|
||||
sendTenantRoomMessage(
|
||||
userId,
|
||||
tenantId,
|
||||
{
|
||||
key: "allgemein",
|
||||
name: "Allgemeiner Chat",
|
||||
},
|
||||
text
|
||||
)
|
||||
|
||||
return {
|
||||
getStatus,
|
||||
matrixUserIdForUser,
|
||||
@@ -906,9 +984,13 @@ export function matrixService(server: FastifyInstance) {
|
||||
provisionCurrentUser,
|
||||
getTenantSpaceStatus,
|
||||
provisionCurrentTenantSpace,
|
||||
listTenantRooms,
|
||||
getTenantRoomStatus,
|
||||
provisionTenantRoom,
|
||||
createAccessTokenForUser,
|
||||
getTenantRoomMessages,
|
||||
getTenantRoomMembers,
|
||||
sendTenantRoomMessage,
|
||||
getGeneralRoomMessages,
|
||||
getGeneralRoomMembers,
|
||||
sendGeneralRoomMessage,
|
||||
|
||||
@@ -3,6 +3,23 @@ import { matrixService } from "../modules/matrix.service"
|
||||
|
||||
export default async function communicationRoutes(server: FastifyInstance) {
|
||||
const matrix = matrixService(server)
|
||||
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 }
|
||||
|
||||
return {
|
||||
key: params.roomKey || body.key,
|
||||
name: body.name,
|
||||
topic: body.topic,
|
||||
}
|
||||
}
|
||||
|
||||
server.get("/communication/matrix/status", async () => {
|
||||
return matrix.getStatus()
|
||||
@@ -21,10 +38,7 @@ export default async function communicationRoutes(server: FastifyInstance) {
|
||||
try {
|
||||
return await matrix.provisionCurrentUser(req.user.user_id, req.user.tenant_id)
|
||||
} catch (err: any) {
|
||||
req.log.error(err)
|
||||
return reply
|
||||
.code(err.statusCode || 500)
|
||||
.send({ error: err.message || "Matrix provisioning failed" })
|
||||
return handleMatrixError(req, reply, err, "Matrix provisioning failed")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -32,10 +46,7 @@ export default async function communicationRoutes(server: FastifyInstance) {
|
||||
try {
|
||||
return await matrix.getTenantSpaceStatus(req.user.tenant_id)
|
||||
} catch (err: any) {
|
||||
req.log.error(err)
|
||||
return reply
|
||||
.code(err.statusCode || 500)
|
||||
.send({ error: err.message || "Matrix tenant space status failed" })
|
||||
return handleMatrixError(req, reply, err, "Matrix tenant space status failed")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -43,10 +54,27 @@ export default async function communicationRoutes(server: FastifyInstance) {
|
||||
try {
|
||||
return await matrix.provisionCurrentTenantSpace(req.user.user_id, req.user.tenant_id)
|
||||
} catch (err: any) {
|
||||
req.log.error(err)
|
||||
return reply
|
||||
.code(err.statusCode || 500)
|
||||
.send({ error: err.message || "Matrix tenant space provisioning failed" })
|
||||
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")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -54,10 +82,7 @@ export default async function communicationRoutes(server: FastifyInstance) {
|
||||
try {
|
||||
return await matrix.getTenantRoomStatus(req.user.tenant_id, "allgemein", "Allgemeiner Chat")
|
||||
} catch (err: any) {
|
||||
req.log.error(err)
|
||||
return reply
|
||||
.code(err.statusCode || 500)
|
||||
.send({ error: err.message || "Matrix room status failed" })
|
||||
return handleMatrixError(req, reply, err, "Matrix room status failed")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -68,10 +93,7 @@ export default async function communicationRoutes(server: FastifyInstance) {
|
||||
name: "Allgemeiner Chat",
|
||||
})
|
||||
} catch (err: any) {
|
||||
req.log.error(err)
|
||||
return reply
|
||||
.code(err.statusCode || 500)
|
||||
.send({ error: err.message || "Matrix room provisioning failed" })
|
||||
return handleMatrixError(req, reply, err, "Matrix room provisioning failed")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -79,10 +101,7 @@ export default async function communicationRoutes(server: FastifyInstance) {
|
||||
try {
|
||||
return await matrix.getGeneralRoomMessages(req.user.user_id, req.user.tenant_id)
|
||||
} catch (err: any) {
|
||||
req.log.error(err)
|
||||
return reply
|
||||
.code(err.statusCode || 500)
|
||||
.send({ error: err.message || "Matrix messages failed" })
|
||||
return handleMatrixError(req, reply, err, "Matrix messages failed")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -90,10 +109,7 @@ export default async function communicationRoutes(server: FastifyInstance) {
|
||||
try {
|
||||
return await matrix.getGeneralRoomMembers(req.user.user_id, req.user.tenant_id)
|
||||
} catch (err: any) {
|
||||
req.log.error(err)
|
||||
return reply
|
||||
.code(err.statusCode || 500)
|
||||
.send({ error: err.message || "Matrix members failed" })
|
||||
return handleMatrixError(req, reply, err, "Matrix members failed")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -102,10 +118,66 @@ export default async function communicationRoutes(server: FastifyInstance) {
|
||||
const body = req.body as { text?: string }
|
||||
return await matrix.sendGeneralRoomMessage(req.user.user_id, req.user.tenant_id, body.text || "")
|
||||
} catch (err: any) {
|
||||
req.log.error(err)
|
||||
return reply
|
||||
.code(err.statusCode || 500)
|
||||
.send({ error: err.message || "Matrix message send failed" })
|
||||
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, 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/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")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user