secubox-openwrt/package/secubox/luci-app-voip/htdocs/luci-static/resources/view/voip/trunks.js
CyberMind-FR 4ca46b61e2 feat(voip): Add VoIP packages with OVH provisioning and Jabber integration
New packages:
- secubox-app-voip: Asterisk PBX in LXC container
- luci-app-voip: Dashboard with extensions, trunks, click-to-call

VoIP features:
- voipctl CLI for container, extensions, trunks, calls, voicemail
- OVH Telephony API auto-provisioning for SIP trunks
- Click-to-call web interface with quick dial
- RPCD backend with 15 methods

Jabber VoIP integration:
- Jingle VoIP support (STUN/TURN via mod_external_services)
- SMS relay via OVH (messages to sms@domain)
- Voicemail notifications via Asterisk AMI → XMPP
- 9 new RPCD methods for VoIP features

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-19 09:22:06 +01:00

151 lines
4.2 KiB
JavaScript

'use strict';
'require view';
'require ui';
'require uci';
'require form';
'require voip.api as api';
return view.extend({
load: function() {
return Promise.all([
api.getStatus(),
api.getTrunkStatus(),
uci.load('voip')
]);
},
render: function(data) {
var status = data[0];
var trunkStatus = data[1];
var running = status.running === 'true';
var view = E('div', { 'class': 'cbi-map' }, [
E('h2', {}, 'SIP Trunks')
]);
// Trunk status section
var registered = trunkStatus.registered === 'true';
view.appendChild(E('div', { 'class': 'cbi-section' }, [
E('h3', {}, 'Trunk Status'),
E('table', { 'class': 'table' }, [
E('tr', { 'class': 'tr' }, [
E('td', { 'class': 'td' }, 'Registration'),
E('td', { 'class': 'td' }, [
E('span', { 'class': 'badge ' + (registered ? 'success' : 'warning') },
registered ? 'Registered' : 'Not Registered')
])
])
]),
trunkStatus.status ? E('pre', {
'style': 'background: #f5f5f5; padding: 10px; font-size: 12px; overflow-x: auto;'
}, trunkStatus.status) : ''
]));
// OVH Auto-provision section
var ovhEnabled = uci.get('voip', 'ovh_telephony', 'enabled') === '1';
var ovhAppKey = uci.get('voip', 'ovh_telephony', 'app_key') || '';
view.appendChild(E('div', { 'class': 'cbi-section' }, [
E('h3', {}, 'OVH Telephony'),
E('p', {}, 'Auto-provision SIP trunk from OVH Telephony API'),
E('table', { 'class': 'table' }, [
E('tr', { 'class': 'tr' }, [
E('td', { 'class': 'td' }, 'API Credentials'),
E('td', { 'class': 'td' }, ovhAppKey ? 'Configured' : 'Not configured')
])
]),
E('div', { 'class': 'cbi-page-actions' }, [
E('button', {
'class': 'btn cbi-button cbi-button-positive',
'disabled': !ovhAppKey || !running,
'click': ui.createHandlerFn(this, function() {
return this.handleOvhProvision();
})
}, 'Auto-Provision OVH Trunk')
])
]));
// UCI configuration form
var m, s, o;
m = new form.Map('voip', null, null);
s = m.section(form.NamedSection, 'ovh_telephony', 'ovh', 'OVH API Settings');
s.anonymous = true;
o = s.option(form.Flag, 'enabled', 'Enable OVH Integration');
o.default = '0';
o = s.option(form.ListValue, 'endpoint', 'API Endpoint');
o.value('ovh-eu', 'OVH Europe');
o.value('ovh-ca', 'OVH Canada');
o.value('ovh-us', 'OVH US');
o.default = 'ovh-eu';
o = s.option(form.Value, 'app_key', 'Application Key');
o.password = false;
o.rmempty = true;
o = s.option(form.Value, 'app_secret', 'Application Secret');
o.password = true;
o.rmempty = true;
o = s.option(form.Value, 'consumer_key', 'Consumer Key');
o.password = true;
o.rmempty = true;
o = s.option(form.Value, 'billing_account', 'Billing Account');
o.rmempty = true;
o.placeholder = '(auto-detected)';
o = s.option(form.Value, 'service_name', 'Service Name (SIP Line)');
o.rmempty = true;
o.placeholder = '(auto-detected)';
// Manual trunk section
s = m.section(form.NamedSection, 'sip_trunk', 'trunk', 'Manual Trunk Configuration');
s.anonymous = true;
o = s.option(form.Flag, 'enabled', 'Enable Trunk');
o.default = '0';
o = s.option(form.Value, 'host', 'SIP Server');
o.placeholder = 'sip.provider.com';
o = s.option(form.Value, 'username', 'Username');
o = s.option(form.Value, 'password', 'Password');
o.password = true;
o = s.option(form.Value, 'outbound_proxy', 'Outbound Proxy');
o.rmempty = true;
o = s.option(form.Value, 'codecs', 'Codecs');
o.default = 'ulaw,alaw,g729';
return m.render().then(function(formEl) {
view.appendChild(formEl);
return view;
});
},
handleOvhProvision: function() {
ui.showModal('Provisioning OVH Trunk', [
E('p', { 'class': 'spinning' }, 'Connecting to OVH API and configuring SIP trunk...')
]);
return api.addOvhTrunk().then(function(res) {
ui.hideModal();
if (res.success) {
ui.addNotification(null, E('p', res.message || 'OVH trunk provisioned'), 'success');
window.location.reload();
} else {
ui.addNotification(null, E('p', 'Failed: ' + (res.error || 'Unknown error')), 'error');
}
}).catch(function(e) {
ui.hideModal();
ui.addNotification(null, E('p', 'Provisioning failed: ' + e.message), 'error');
});
}
});