KI-AGENT: Matrix-Anrufe im Chat vorbereiten
This commit is contained in:
@@ -89,6 +89,8 @@ MATRIX_DEV_TURN_MAX_PORT=49200
|
||||
|
||||
# Backend-Integration gegen den lokalen Matrix-Stack
|
||||
MATRIX_HOMESERVER_URL=http://localhost:8008
|
||||
MATRIX_RTC_JWT_URL=http://localhost:8081
|
||||
MATRIX_LIVEKIT_URL=ws://localhost:7880
|
||||
MATRIX_REGISTRATION_SHARED_SECRET=copy-from-matrix-dev-synapse-homeserver-yaml
|
||||
MATRIX_SERVICE_USER_LOCALPART=fedeo_service
|
||||
NUXT_PUBLIC_MATRIX_ELEMENT_URL=http://localhost:8080
|
||||
|
||||
@@ -129,6 +129,25 @@ export function matrixService(server: FastifyInstance) {
|
||||
readLocalDevRegistrationSharedSecret() ||
|
||||
""
|
||||
|
||||
const rtcHost = () =>
|
||||
process.env.MATRIX_RTC_HOST ||
|
||||
secrets.MATRIX_RTC_HOST ||
|
||||
"call.fedeo.de"
|
||||
|
||||
const rtcJwtUrl = () =>
|
||||
process.env.MATRIX_RTC_JWT_URL ||
|
||||
secrets.MATRIX_RTC_JWT_URL ||
|
||||
(process.env.NODE_ENV === "production"
|
||||
? `https://${rtcHost()}/livekit/jwt`
|
||||
: `http://localhost:${process.env.MATRIX_DEV_RTC_JWT_PORT || "8081"}`)
|
||||
|
||||
const livekitUrl = () =>
|
||||
process.env.MATRIX_LIVEKIT_URL ||
|
||||
secrets.MATRIX_LIVEKIT_URL ||
|
||||
(process.env.NODE_ENV === "production"
|
||||
? `wss://${rtcHost()}/livekit/sfu`
|
||||
: `ws://localhost:${process.env.MATRIX_DEV_LIVEKIT_PORT || "7880"}`)
|
||||
|
||||
const serviceUserLocalpart = () =>
|
||||
process.env.MATRIX_SERVICE_USER_LOCALPART ||
|
||||
secrets.MATRIX_SERVICE_USER_LOCALPART ||
|
||||
@@ -404,6 +423,13 @@ export function matrixService(server: FastifyInstance) {
|
||||
serverName: serverName(),
|
||||
provisioningConfigured: Boolean(registrationSharedSecret()),
|
||||
reachable: false,
|
||||
calls: {
|
||||
provider: "matrixrtc-livekit",
|
||||
configured: Boolean(rtcJwtUrl() && livekitUrl()),
|
||||
rtcHost: rtcHost(),
|
||||
rtcJwtUrl: rtcJwtUrl(),
|
||||
livekitUrl: livekitUrl(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,6 +444,13 @@ export function matrixService(server: FastifyInstance) {
|
||||
serverName: serverName(),
|
||||
provisioningConfigured: Boolean(registrationSharedSecret()),
|
||||
reachable: true,
|
||||
calls: {
|
||||
provider: "matrixrtc-livekit",
|
||||
configured: Boolean(rtcJwtUrl() && livekitUrl()),
|
||||
rtcHost: rtcHost(),
|
||||
rtcJwtUrl: rtcJwtUrl(),
|
||||
livekitUrl: livekitUrl(),
|
||||
},
|
||||
versions: versions.versions,
|
||||
}
|
||||
} catch (err: any) {
|
||||
@@ -427,6 +460,13 @@ export function matrixService(server: FastifyInstance) {
|
||||
serverName: serverName(),
|
||||
provisioningConfigured: Boolean(registrationSharedSecret()),
|
||||
reachable: false,
|
||||
calls: {
|
||||
provider: "matrixrtc-livekit",
|
||||
configured: Boolean(rtcJwtUrl() && livekitUrl()),
|
||||
rtcHost: rtcHost(),
|
||||
rtcJwtUrl: rtcJwtUrl(),
|
||||
livekitUrl: livekitUrl(),
|
||||
},
|
||||
error: err.message,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,9 @@ export let secrets = {
|
||||
STIRLING_API_KEY: string
|
||||
MATRIX_HOMESERVER_URL?: string
|
||||
MATRIX_SERVER_NAME?: string
|
||||
MATRIX_RTC_HOST?: string
|
||||
MATRIX_RTC_JWT_URL?: string
|
||||
MATRIX_LIVEKIT_URL?: string
|
||||
MATRIX_REGISTRATION_SHARED_SECRET?: string
|
||||
MATRIX_SERVICE_USER_LOCALPART?: string
|
||||
}
|
||||
@@ -76,6 +79,9 @@ const secretKeys = [
|
||||
"STIRLING_API_KEY",
|
||||
"MATRIX_HOMESERVER_URL",
|
||||
"MATRIX_SERVER_NAME",
|
||||
"MATRIX_RTC_HOST",
|
||||
"MATRIX_RTC_JWT_URL",
|
||||
"MATRIX_LIVEKIT_URL",
|
||||
"MATRIX_REGISTRATION_SHARED_SECRET",
|
||||
"MATRIX_SERVICE_USER_LOCALPART",
|
||||
] as const
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup>
|
||||
const toast = useToast()
|
||||
const { $api } = useNuxtApp()
|
||||
const runtimeConfig = useRuntimeConfig()
|
||||
|
||||
const status = ref(null)
|
||||
const identity = ref(null)
|
||||
@@ -11,6 +12,8 @@ const matrixMembers = ref([])
|
||||
const matrixMessageDraft = ref("")
|
||||
const matrixMessagesViewport = ref(null)
|
||||
const roomCreateOpen = ref(false)
|
||||
const matrixCallOpen = ref(false)
|
||||
const matrixCallMode = ref("video")
|
||||
const roomCreateForm = ref({
|
||||
name: "",
|
||||
key: "",
|
||||
@@ -48,6 +51,28 @@ const activeRoomEndpoint = computed(() =>
|
||||
`/api/communication/matrix/rooms/${encodeURIComponent(activeRoomKey.value)}`
|
||||
)
|
||||
|
||||
const matrixElementUrl = computed(() =>
|
||||
String(runtimeConfig.public?.matrixElementUrl || "").replace(/\/+$/, "")
|
||||
)
|
||||
|
||||
const activeRoomMatrixAddress = computed(() =>
|
||||
activeRoom.value?.roomId || activeRoom.value?.alias || ""
|
||||
)
|
||||
|
||||
const activeRoomElementUrl = computed(() => {
|
||||
if (!matrixElementUrl.value || !activeRoomMatrixAddress.value) return ""
|
||||
|
||||
return `${matrixElementUrl.value}/#/room/${encodeURIComponent(activeRoomMatrixAddress.value)}`
|
||||
})
|
||||
|
||||
const canStartMatrixCall = computed(() =>
|
||||
Boolean(canUseMatrixChat.value && activeRoom.value?.exists && activeRoomElementUrl.value)
|
||||
)
|
||||
|
||||
const matrixCallTitle = computed(() =>
|
||||
matrixCallMode.value === "audio" ? "Audioanruf" : "Videokonferenz"
|
||||
)
|
||||
|
||||
const roomCreateKeyPreview = computed(() =>
|
||||
normalizeRoomKey(roomCreateForm.value.key || roomCreateForm.value.name)
|
||||
)
|
||||
@@ -315,6 +340,19 @@ const syncRoomMembers = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const openMatrixCall = (mode = "video") => {
|
||||
if (!canStartMatrixCall.value) {
|
||||
toast.add({
|
||||
title: "Besprechung kann noch nicht gestartet werden",
|
||||
color: "warning"
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
matrixCallMode.value = mode
|
||||
matrixCallOpen.value = true
|
||||
}
|
||||
|
||||
const loadRoomChat = async ({ silent = false, includeMembers = false } = {}) => {
|
||||
await loadRoomMessages({ silent })
|
||||
|
||||
@@ -576,6 +614,22 @@ onBeforeUnmount(stopMatrixAutoRefresh)
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<UButton
|
||||
icon="i-heroicons-phone"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
aria-label="Audioanruf starten"
|
||||
:disabled="!canStartMatrixCall"
|
||||
@click="openMatrixCall('audio')"
|
||||
/>
|
||||
<UButton
|
||||
icon="i-heroicons-video-camera"
|
||||
color="primary"
|
||||
variant="soft"
|
||||
aria-label="Videokonferenz starten"
|
||||
:disabled="!canStartMatrixCall"
|
||||
@click="openMatrixCall('video')"
|
||||
/>
|
||||
<UButton
|
||||
class="lg:hidden"
|
||||
to="/communication"
|
||||
@@ -829,6 +883,34 @@ onBeforeUnmount(stopMatrixAutoRefresh)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 class="mb-2 text-xs font-medium uppercase text-muted">
|
||||
Besprechung
|
||||
</h4>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<UButton
|
||||
icon="i-heroicons-phone"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
block
|
||||
:disabled="!canStartMatrixCall"
|
||||
@click="openMatrixCall('audio')"
|
||||
>
|
||||
Audio
|
||||
</UButton>
|
||||
<UButton
|
||||
icon="i-heroicons-video-camera"
|
||||
color="primary"
|
||||
variant="soft"
|
||||
block
|
||||
:disabled="!canStartMatrixCall"
|
||||
@click="openMatrixCall('video')"
|
||||
>
|
||||
Video
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 class="mb-2 text-xs font-medium uppercase text-muted">
|
||||
Eigene Identität
|
||||
@@ -839,5 +921,67 @@ onBeforeUnmount(stopMatrixAutoRefresh)
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<UModal
|
||||
v-model:open="matrixCallOpen"
|
||||
fullscreen
|
||||
class="h-[100dvh]"
|
||||
>
|
||||
<template #content>
|
||||
<div class="flex h-[100dvh] flex-col bg-default">
|
||||
<header class="flex shrink-0 items-center justify-between gap-3 border-b border-default px-4 py-3">
|
||||
<div class="min-w-0">
|
||||
<h3 class="truncate text-base font-semibold text-highlighted">
|
||||
{{ matrixCallTitle }} · {{ activeRoom.name }}
|
||||
</h3>
|
||||
<p class="truncate text-xs text-muted">
|
||||
{{ activeRoomMatrixAddress }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<UButton
|
||||
v-if="activeRoomElementUrl"
|
||||
:to="activeRoomElementUrl"
|
||||
target="_blank"
|
||||
icon="i-heroicons-arrow-top-right-on-square"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
>
|
||||
Extern öffnen
|
||||
</UButton>
|
||||
<UButton
|
||||
icon="i-heroicons-x-mark"
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
@click="matrixCallOpen = false"
|
||||
/>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="min-h-0 flex-1 bg-muted">
|
||||
<iframe
|
||||
v-if="canStartMatrixCall"
|
||||
:key="`${activeRoomKey}-${matrixCallMode}`"
|
||||
:src="activeRoomElementUrl"
|
||||
class="h-full w-full border-0"
|
||||
allow="camera; microphone; display-capture; clipboard-read; clipboard-write; fullscreen; autoplay"
|
||||
referrerpolicy="no-referrer"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
class="flex h-full items-center justify-center p-6"
|
||||
>
|
||||
<UAlert
|
||||
icon="i-heroicons-exclamation-triangle"
|
||||
color="warning"
|
||||
variant="soft"
|
||||
title="Besprechung nicht verfügbar"
|
||||
description="Der Matrix-Raum oder die Element-Integration ist noch nicht bereit."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</UModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -45,6 +45,12 @@ const statusItems = computed(() => [
|
||||
value: status.value?.reachable ? "Erreichbar" : "Nicht erreichbar",
|
||||
icon: status.value?.reachable ? "i-heroicons-signal" : "i-heroicons-signal-slash",
|
||||
color: status.value?.reachable ? "success" : "error"
|
||||
},
|
||||
{
|
||||
label: "Audio/Video",
|
||||
value: status.value?.calls?.configured ? "Bereit" : "Nicht eingerichtet",
|
||||
icon: status.value?.calls?.configured ? "i-heroicons-video-camera" : "i-heroicons-video-camera-slash",
|
||||
color: status.value?.calls?.configured ? "success" : "warning"
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user