pulse-lets-go/docs/freeswitch.md
Maksim Totmin 12a51a5ebe docs: restructure documentation into docs/ directory
- Create docs/ with 7 focused files: architecture, api, scoring, agent,
  deployment, freeswitch, development
- Rewrite README.md as concise overview with links to docs/
- Remove AGENTS.md (content merged into docs/ and README)
2026-06-25 22:35:19 +07:00

105 lines
3.0 KiB
Markdown

# FreeSWITCH Integration
pulse-lets-go routes calls through FreeSWITCH via `mod_curl` → Lua dialplan → `/api/route`.
## Dialplan (route.lua)
FreeSWITCH uses a Lua script in its dialplan. The script calls `/api/route` via `mod_curl` and bridges to the returned gateway.
```lua
-- contrib/route.lua (simplified)
api = freeswitch.API()
caller_id = session:getVariable("caller_id_number") or ""
dest = session:getVariable("destination_number") or ""
url = "http://localhost:8080/api/route?caller_id=" .. caller_id .. "&dest=" .. dest
raw = api:execute("curl", url)
body = raw:match("\r?\n\r?\n(.+)") or raw
local ok, route = pcall(cjson.decode, body)
if not ok then session:hangup("NORMAL_TEMPORARY_FAILURE") return end
if route.fallback then
session:bridge(route.sip_gateway) -- operator
elseif route.sip_gateway then
session:bridge(route.sip_gateway) -- best node
else
session:hangup("NORMAL_TEMPORARY_FAILURE")
end
```
## Dialplan Configuration
```xml
<extension name="route_call">
<condition field="destination_number" expression="^.*$">
<action application="set" data="balancer_url=http://balancer.lan:8080"/>
<action application="set" data="ingress_trunk=trk-001"/>
<action application="lua" data="route.lua"/>
</condition>
</extension>
```
Reload dialplan:
```bash
sudo fs_cli -x "reloadxml"
```
## ESL (Gateway Management)
When `esl.host` is configured in `config.json`, the balancer:
- Connects to FreeSWITCH ESL (`:8021`)
- Synchronizes ingress trunks as Sofia gateways
- Listens for gateway registration events
- Streams gateway status to WebSocket frontend
### Configuration
```json
{
"esl": {
"host": "127.0.0.1",
"port": 8021,
"password": "ClueCon",
"password_env": "",
"sofia_profile": "external",
"gateway_prefix": "pulse"
}
}
```
- `password_env` — alternative: name of environment variable containing password
- `sofia_profile` — Sofia profile to manage (default: `external`)
- `gateway_prefix` — prefix for auto-created gateways (default: `pulse`)
## Prerequisites (before deployment)
| # | Check | Command | Expected |
|:-:|-------|---------|----------|
| 1 | **ESL running** | `nc -z 127.0.0.1 8021 && echo OK` | `OK` |
| 2 | **ESL password** | `echo "auth ClueCon" \| nc 127.0.0.1 8021` | `+OK accepted` |
| 3 | **mod_curl loaded** | `fs_cli -x "show modules" \| grep mod_curl` | `<load module="mod_curl"/>` |
| 4 | **mod_lua loaded** | `fs_cli -x "show modules" \| grep mod_lua` | `<load module="mod_lua"/>` |
| 5 | **ESL ACL** | ACL in `event_socket.conf.xml` allows 127.0.0.1 | `localhost` or `127.0.0.1` in ACL |
## Loading mod_curl Without Restart
```bash
fs_cli -x "load mod_curl"
# Add to modules.conf.xml for persistence:
# <load module="mod_curl"/>
```
## Rollback (if route.lua breaks calls)
```bash
# 1. Remove pulse_route from dialplan
sudo sed -i '/pulse_route/,/<\/extension>/d' /etc/freeswitch/dialplan/default.xml
sudo fs_cli -x "reloadxml"
# 2. Stop pulse-lets-go
sudo systemctl stop pulse-lets-go
# Calls continue through original dialplan without balancer.
```