KI-AGENT: NAT Einstellungen für Telekom-Trunk ergänzen
This commit is contained in:
3
backend/db/migrations/0046_telephony_trunk_nat.sql
Normal file
3
backend/db/migrations/0046_telephony_trunk_nat.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE "telephony_trunks" ADD COLUMN IF NOT EXISTS "external_signaling_address" text;
|
||||
ALTER TABLE "telephony_trunks" ADD COLUMN IF NOT EXISTS "external_media_address" text;
|
||||
ALTER TABLE "telephony_trunks" ADD COLUMN IF NOT EXISTS "local_networks" text;
|
||||
@@ -323,6 +323,13 @@
|
||||
"when": 1780164000000,
|
||||
"tag": "0045_telephony_trunks",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 46,
|
||||
"version": "7",
|
||||
"when": 1780167600000,
|
||||
"tag": "0046_telephony_trunk_nat",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -30,6 +30,9 @@ export const telephonyTrunks = pgTable(
|
||||
callerId: text("caller_id"),
|
||||
inboundExtension: text("inbound_extension").notNull().default("1001"),
|
||||
outboundPrefix: text("outbound_prefix").notNull().default("0"),
|
||||
externalSignalingAddress: text("external_signaling_address"),
|
||||
externalMediaAddress: text("external_media_address"),
|
||||
localNetworks: text("local_networks"),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
|
||||
@@ -75,6 +75,9 @@ const sanitizeTrunk = (trunk: any) => ({
|
||||
callerId: trunk?.callerId || "",
|
||||
inboundExtension: trunk?.inboundExtension || "1001",
|
||||
outboundPrefix: trunk?.outboundPrefix || "0",
|
||||
externalSignalingAddress: trunk?.externalSignalingAddress || "",
|
||||
externalMediaAddress: trunk?.externalMediaAddress || "",
|
||||
localNetworks: trunk?.localNetworks || "172.16.0.0/12,192.168.0.0/16,10.0.0.0/8",
|
||||
})
|
||||
|
||||
const envExternalTelephonyConfig = () => {
|
||||
@@ -157,6 +160,7 @@ const renderTelekomPjsipConfig = (trunk: any) => {
|
||||
const authUser = asteriskValue(trunk.authUser) || sipUser
|
||||
const password = asteriskValue(trunk.password)
|
||||
const callerId = asteriskValue(trunk.callerId) || sipUser
|
||||
const externalMediaAddress = asteriskValue(trunk.externalMediaAddress || trunk.externalSignalingAddress)
|
||||
|
||||
return [
|
||||
"; Von FEDEO generiert. Änderungen im Container können überschrieben werden.",
|
||||
@@ -181,6 +185,7 @@ const renderTelekomPjsipConfig = (trunk: any) => {
|
||||
`from_user=${sipUser}`,
|
||||
`from_domain=${registrar}`,
|
||||
`callerid=Telekom <${callerId}>`,
|
||||
...(externalMediaAddress ? [`media_address=${externalMediaAddress}`] : []),
|
||||
"direct_media=no",
|
||||
"force_rport=yes",
|
||||
"rewrite_contact=yes",
|
||||
@@ -249,6 +254,35 @@ const renderTelekomExtensionsConfig = (trunk: any) => {
|
||||
].join("\n")
|
||||
}
|
||||
|
||||
const renderTelekomTransportConfig = (trunk: any) => {
|
||||
const externalSignalingAddress = asteriskValue(trunk?.externalSignalingAddress)
|
||||
const externalMediaAddress = asteriskValue(trunk?.externalMediaAddress || trunk?.externalSignalingAddress)
|
||||
const localNetworks = asteriskValue(trunk?.localNetworks || "172.16.0.0/12,192.168.0.0/16,10.0.0.0/8")
|
||||
.split(/[\s,;]+/)
|
||||
.map((entry) => entry.trim())
|
||||
.filter(Boolean)
|
||||
|
||||
if (!externalSignalingAddress && !externalMediaAddress) {
|
||||
return [
|
||||
"; Von FEDEO generiert.",
|
||||
"; Kein externes Asterisk-NAT-Rewrite konfiguriert.",
|
||||
"",
|
||||
].join("\n")
|
||||
}
|
||||
|
||||
return [
|
||||
"; Von FEDEO generiert. Änderungen am Transport benötigen einen Asterisk-Neustart.",
|
||||
"[transport-udp](+)",
|
||||
...(externalSignalingAddress ? [
|
||||
`external_signaling_address=${externalSignalingAddress}`,
|
||||
"external_signaling_port=5060",
|
||||
] : []),
|
||||
...(externalMediaAddress ? [`external_media_address=${externalMediaAddress}`] : []),
|
||||
...localNetworks.map((network) => `local_net=${network}`),
|
||||
"",
|
||||
].join("\n")
|
||||
}
|
||||
|
||||
const writeAsteriskTrunkConfig = async (trunk: any) => {
|
||||
const targetDir = asteriskGeneratedDir()
|
||||
await fs.mkdir(targetDir, { recursive: true })
|
||||
@@ -262,6 +296,10 @@ const writeAsteriskTrunkConfig = async (trunk: any) => {
|
||||
name: "extensions.telekom.conf",
|
||||
content: renderTelekomExtensionsConfig(trunk),
|
||||
},
|
||||
{
|
||||
name: "pjsip.transport.conf",
|
||||
content: renderTelekomTransportConfig(trunk),
|
||||
},
|
||||
]
|
||||
|
||||
await Promise.all(files.map(async (file) => {
|
||||
@@ -364,10 +402,13 @@ export default async function telephonyRoutes(server: FastifyInstance) {
|
||||
return {
|
||||
enabled: trunk.enabled,
|
||||
provider: trunk.provider,
|
||||
inboundExtension: trunk.inboundExtension,
|
||||
outboundPrefix: trunk.outboundPrefix,
|
||||
registrar: trunk.registrar,
|
||||
sipUserConfigured: Boolean(trunk.sipUser),
|
||||
inboundExtension: trunk.inboundExtension,
|
||||
outboundPrefix: trunk.outboundPrefix,
|
||||
registrar: trunk.registrar,
|
||||
externalSignalingAddress: trunk.externalSignalingAddress,
|
||||
externalMediaAddress: trunk.externalMediaAddress,
|
||||
localNetworks: trunk.localNetworks,
|
||||
sipUserConfigured: Boolean(trunk.sipUser),
|
||||
authUserConfigured: Boolean(trunk.authUser),
|
||||
passwordConfigured: Boolean(trunk.password),
|
||||
callerIdConfigured: Boolean(trunk.callerId),
|
||||
@@ -463,6 +504,9 @@ export default async function telephonyRoutes(server: FastifyInstance) {
|
||||
callerId: bodyString(body, "callerId"),
|
||||
inboundExtension: bodyString(body, "inboundExtension") || "1001",
|
||||
outboundPrefix: bodyString(body, "outboundPrefix") || "0",
|
||||
externalSignalingAddress: bodyString(body, "externalSignalingAddress"),
|
||||
externalMediaAddress: bodyString(body, "externalMediaAddress"),
|
||||
localNetworks: bodyString(body, "localNetworks") || "172.16.0.0/12,192.168.0.0/16,10.0.0.0/8",
|
||||
password: clearPassword ? null : (password || existing?.password || null),
|
||||
updatedAt: now,
|
||||
updatedBy: req.user.user_id,
|
||||
|
||||
@@ -22,11 +22,16 @@ TELEPHONY_TELEKOM_AUTH_USER=<anschlusskennung><zugangsnummer>#<mitbenutzernummer
|
||||
- `Absendernummer`: optional, meist identisch zur SIP-ID
|
||||
- `Eingehende Nebenstelle`: lokale FEDEO/Asterisk-Nebenstelle, z. B. `1001`
|
||||
- `Ausgehender Prefix`: standardmäßig `0`
|
||||
- `Öffentliche Signaling-Adresse`: öffentliche IP oder DNS-Name, unter dem Asterisk von Telekom erreichbar ist
|
||||
- `Öffentliche Medien-Adresse`: öffentliche RTP-Adresse, leer nutzt die Signaling-Adresse
|
||||
- `Lokale Netze`: interne Netze, für die Asterisk keine öffentliche Adresse einsetzen soll
|
||||
|
||||
Das Kennwort wird nicht wieder an das Frontend zurückgegeben. In der Oberfläche siehst du nur, ob ein Kennwort gespeichert ist.
|
||||
|
||||
Nach dem Speichern muss die Konfiguration mit **In Asterisk anwenden** in die Asterisk-Include-Dateien geschrieben werden. FEDEO lädt danach PJSIP und den Dialplan über AMI neu und fordert die Telekom-Registration an. Den Laufzeitstatus findest du unter **Kommunikation -> Telefonie Setup** im Bereich **Externe Telefonie**.
|
||||
|
||||
Wenn du die öffentliche Signaling- oder Medien-Adresse änderst, muss Asterisk anschließend neu gestartet werden, weil PJSIP-Transporte diese Werte nicht vollständig im laufenden Betrieb neu laden.
|
||||
|
||||
## `.env` Fallback
|
||||
|
||||
Die `.env`-Werte bleiben nur als lokaler Fallback für den Asterisk-Teststack erhalten, falls keine mandantenbezogene Konfiguration gepflegt ist.
|
||||
|
||||
@@ -145,7 +145,10 @@ const telephonyTrunkForm = reactive({
|
||||
clearPassword: false,
|
||||
callerId: "",
|
||||
inboundExtension: "1001",
|
||||
outboundPrefix: "0"
|
||||
outboundPrefix: "0",
|
||||
externalSignalingAddress: "",
|
||||
externalMediaAddress: "",
|
||||
localNetworks: "172.16.0.0/12,192.168.0.0/16,10.0.0.0/8"
|
||||
})
|
||||
|
||||
const setupPage = async () => {
|
||||
@@ -223,6 +226,9 @@ const loadTelephonyTrunk = async () => {
|
||||
telephonyTrunkForm.callerId = res?.callerId || ""
|
||||
telephonyTrunkForm.inboundExtension = res?.inboundExtension || "1001"
|
||||
telephonyTrunkForm.outboundPrefix = res?.outboundPrefix || "0"
|
||||
telephonyTrunkForm.externalSignalingAddress = res?.externalSignalingAddress || ""
|
||||
telephonyTrunkForm.externalMediaAddress = res?.externalMediaAddress || ""
|
||||
telephonyTrunkForm.localNetworks = res?.localNetworks || "172.16.0.0/12,192.168.0.0/16,10.0.0.0/8"
|
||||
} catch (error) {
|
||||
toast.add({ title: "Telefonie-Trunk konnte nicht geladen werden", color: "error" })
|
||||
} finally {
|
||||
@@ -254,7 +260,10 @@ const saveTelephonyTrunk = async () => {
|
||||
clearPassword: telephonyTrunkForm.clearPassword,
|
||||
callerId: telephonyTrunkForm.callerId,
|
||||
inboundExtension: telephonyTrunkForm.inboundExtension,
|
||||
outboundPrefix: telephonyTrunkForm.outboundPrefix
|
||||
outboundPrefix: telephonyTrunkForm.outboundPrefix,
|
||||
externalSignalingAddress: telephonyTrunkForm.externalSignalingAddress,
|
||||
externalMediaAddress: telephonyTrunkForm.externalMediaAddress,
|
||||
localNetworks: telephonyTrunkForm.localNetworks
|
||||
}
|
||||
})
|
||||
|
||||
@@ -523,6 +532,15 @@ onMounted(() => {
|
||||
<UFormField label="Ausgehender Prefix">
|
||||
<UInput v-model="telephonyTrunkForm.outboundPrefix" placeholder="0" />
|
||||
</UFormField>
|
||||
<UFormField label="Öffentliche Signaling-Adresse">
|
||||
<UInput v-model="telephonyTrunkForm.externalSignalingAddress" placeholder="Öffentliche IP oder DNS-Name" />
|
||||
</UFormField>
|
||||
<UFormField label="Öffentliche Medien-Adresse">
|
||||
<UInput v-model="telephonyTrunkForm.externalMediaAddress" placeholder="Leer = Signaling-Adresse" />
|
||||
</UFormField>
|
||||
<UFormField label="Lokale Netze">
|
||||
<UInput v-model="telephonyTrunkForm.localNetworks" placeholder="172.16.0.0/12,192.168.0.0/16,10.0.0.0/8" />
|
||||
</UFormField>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2">
|
||||
|
||||
@@ -6,6 +6,7 @@ user_agent=FEDEO Local Asterisk
|
||||
type=transport
|
||||
protocol=udp
|
||||
bind=0.0.0.0:5060
|
||||
#tryinclude generated/pjsip.transport.conf
|
||||
|
||||
[transport-ws]
|
||||
type=transport
|
||||
|
||||
Reference in New Issue
Block a user