KI-AGENT: Anrufhistorie für Telefonie ergänzen

This commit is contained in:
2026-05-21 15:54:24 +02:00
parent ba12c46c88
commit 9e7b5bc0b9
7 changed files with 483 additions and 0 deletions

View File

@@ -1,4 +1,6 @@
import { FastifyInstance } from "fastify"
import { and, desc, eq } from "drizzle-orm"
import { telephonyCalls } from "../../db/schema"
const envFlag = (value: string | undefined, fallback: boolean) => {
if (value === undefined || value === "") return fallback
@@ -52,6 +54,32 @@ const fetchWithTimeout = async (url: string, timeoutMs = 2500) => {
}
}
const requireTenant = (tenantId: number | null) => {
if (!tenantId) {
throw Object.assign(new Error("Kein aktiver Mandant"), { statusCode: 400 })
}
return tenantId
}
const bodyString = (body: any, key: string) => {
const value = body?.[key]
return typeof value === "string" && value.trim() ? value.trim() : null
}
const bodyDate = (body: any, key: string) => {
const value = body?.[key]
if (!value) return null
const date = new Date(value)
return Number.isNaN(date.getTime()) ? null : date
}
const durationSeconds = (startedAt?: Date | null, endedAt?: Date | null) => {
if (!startedAt || !endedAt) return null
return Math.max(0, Math.round((endedAt.getTime() - startedAt.getTime()) / 1000))
}
export default async function telephonyRoutes(server: FastifyInstance) {
server.get("/telephony/config", async () => ({
enabled: telephonyEnabled(),
@@ -113,4 +141,93 @@ export default async function telephonyRoutes(server: FastifyInstance) {
: (lastError?.message || "Asterisk ist nicht erreichbar."),
}
})
server.get("/telephony/calls", async (req) => {
const tenantId = requireTenant(req.user.tenant_id)
const limit = Math.min(
Math.max(Number((req.query as { limit?: string })?.limit || 25), 1),
100
)
return await server.db
.select()
.from(telephonyCalls)
.where(eq(telephonyCalls.tenantId, tenantId))
.orderBy(desc(telephonyCalls.startedAt))
.limit(limit)
})
server.post("/telephony/calls", async (req, reply) => {
const tenantId = requireTenant(req.user.tenant_id)
const body = (req.body || {}) as any
const now = new Date()
const startedAt = bodyDate(body, "startedAt") || now
const direction = bodyString(body, "direction") === "incoming" ? "incoming" : "outgoing"
const status = bodyString(body, "status") || (direction === "incoming" ? "ringing" : "dialing")
const [created] = await server.db
.insert(telephonyCalls)
.values({
tenantId,
direction,
status,
localExtension: bodyString(body, "localExtension"),
remoteNumber: bodyString(body, "remoteNumber"),
remoteDisplayName: bodyString(body, "remoteDisplayName"),
sipCallId: bodyString(body, "sipCallId"),
startedAt,
createdBy: req.user.user_id,
})
.returning()
return reply.code(201).send(created)
})
server.patch("/telephony/calls/:id", async (req, reply) => {
const tenantId = requireTenant(req.user.tenant_id)
const params = req.params as { id: string }
const body = (req.body || {}) as any
const [existing] = await server.db
.select()
.from(telephonyCalls)
.where(and(
eq(telephonyCalls.tenantId, tenantId),
eq(telephonyCalls.id, params.id)
))
.limit(1)
if (!existing) {
return reply.code(404).send({ error: "Anruf nicht gefunden" })
}
const answeredAt = bodyDate(body, "answeredAt")
const endedAt = bodyDate(body, "endedAt")
const startedAt = existing.startedAt ? new Date(existing.startedAt) : null
const computedDuration = endedAt
? durationSeconds(answeredAt || startedAt, endedAt)
: null
const [updated] = await server.db
.update(telephonyCalls)
.set({
status: bodyString(body, "status") || existing.status,
localExtension: bodyString(body, "localExtension") || existing.localExtension,
remoteNumber: bodyString(body, "remoteNumber") || existing.remoteNumber,
remoteDisplayName: bodyString(body, "remoteDisplayName") || existing.remoteDisplayName,
sipCallId: bodyString(body, "sipCallId") || existing.sipCallId,
answeredAt: answeredAt || existing.answeredAt,
endedAt: endedAt || existing.endedAt,
durationSeconds: computedDuration ?? existing.durationSeconds,
updatedAt: new Date(),
updatedBy: req.user.user_id,
})
.where(and(
eq(telephonyCalls.tenantId, tenantId),
eq(telephonyCalls.id, params.id)
))
.returning()
return updated
})
}