diff --git a/.claude/settings.local.json b/.claude/settings.local.json index f4e77524..2c3effaf 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -388,7 +388,8 @@ "Bash(__NEW_LINE_755a36c329effceb__ echo \"\")", "Bash(__NEW_LINE_02bd2dd51e90cbf8__ echo \"\")", "Bash(__NEW_LINE_70eb6f3ae1c26753__ echo \"\")", - "WebFetch(domain:radio.gk2.secubox.in)" + "WebFetch(domain:radio.gk2.secubox.in)", + "WebFetch(domain:nextcloud-talk.readthedocs.io)" ] } } diff --git a/package/secubox/luci-app-turn/htdocs/luci-static/resources/view/turn/overview.js b/package/secubox/luci-app-turn/htdocs/luci-static/resources/view/turn/overview.js index eeeab8fe..6f4e7e0e 100644 --- a/package/secubox/luci-app-turn/htdocs/luci-static/resources/view/turn/overview.js +++ b/package/secubox/luci-app-turn/htdocs/luci-static/resources/view/turn/overview.js @@ -10,6 +10,7 @@ var callStop = rpc.declare({ object: 'luci.turn', method: 'stop', expect: {} }); var callEnable = rpc.declare({ object: 'luci.turn', method: 'enable', expect: {} }); var callDisable = rpc.declare({ object: 'luci.turn', method: 'disable', expect: {} }); var callSetupJitsi = rpc.declare({ object: 'luci.turn', method: 'setup_jitsi', params: ['jitsi_domain', 'turn_domain'], expect: {} }); +var callSetupNextcloud = rpc.declare({ object: 'luci.turn', method: 'setup_nextcloud', params: ['turn_domain', 'use_port_443'], expect: {} }); var callSSL = rpc.declare({ object: 'luci.turn', method: 'ssl', params: ['domain'], expect: {} }); var callExpose = rpc.declare({ object: 'luci.turn', method: 'expose', params: ['domain'], expect: {} }); var callCredentials = rpc.declare({ object: 'luci.turn', method: 'credentials', params: ['username', 'ttl'], expect: {} }); @@ -76,6 +77,16 @@ return view.extend({ ]) ]), + E('div', { 'class': 'sb-section' }, [ + E('h3', {}, 'Nextcloud Talk'), + E('p', {}, 'Configure TURN for Nextcloud Talk (uses port 443 for firewall compatibility)'), + E('div', { 'class': 'form-row' }, [ + E('input', { 'type': 'text', 'id': 'nc-turn-domain', 'placeholder': 'turn.secubox.in', 'class': 'sb-input' }), + E('button', { 'class': 'sb-btn sb-btn-primary', 'click': ui.createHandlerFn(this, 'handleSetupNextcloud') }, 'Setup for Nextcloud') + ]), + E('pre', { 'id': 'nextcloud-output', 'class': 'sb-output', 'style': 'display:none;' }, '') + ]), + E('div', { 'class': 'sb-section' }, [ E('h3', {}, 'SSL & Expose'), E('div', { 'class': 'form-row' }, [ @@ -153,6 +164,22 @@ return view.extend({ }); }, + handleSetupNextcloud: function() { + var turnDomain = document.getElementById('nc-turn-domain').value || 'turn.secubox.in'; + + return callSetupNextcloud(turnDomain, 'yes').then(function(res) { + var output = document.getElementById('nextcloud-output'); + output.style.display = 'block'; + output.textContent = 'Nextcloud Talk Admin Settings:\n\n' + + 'STUN servers: ' + turnDomain + ':' + (res.stun_port || 3478) + '\n' + + 'TURN server: ' + turnDomain + ':' + (res.tls_port || 443) + '\n' + + 'TURN secret: ' + (res.auth_secret || '') + '\n' + + 'Protocol: UDP and TCP\n\n' + + 'Note: Do NOT add turn:// or turns:// prefix'; + ui.addNotification(null, E('p', 'TURN configured for Nextcloud Talk on port ' + (res.tls_port || 443))); + }); + }, + handleSSL: function() { var domain = document.getElementById('ssl-domain').value || 'turn.secubox.in'; return callSSL(domain).then(function(res) { diff --git a/package/secubox/luci-app-turn/root/usr/libexec/rpcd/luci.turn b/package/secubox/luci-app-turn/root/usr/libexec/rpcd/luci.turn index 078e21ba..8b9072fe 100644 --- a/package/secubox/luci-app-turn/root/usr/libexec/rpcd/luci.turn +++ b/package/secubox/luci-app-turn/root/usr/libexec/rpcd/luci.turn @@ -7,7 +7,7 @@ uci_get() { uci -q get "turn.$1" 2>/dev/null || echo "$2"; } case "$1" in list) - echo '{"status":{},"logs":{"lines":50},"test":{"host":""},"start":{},"stop":{},"restart":{},"enable":{},"disable":{},"setup_jitsi":{"jitsi_domain":"","turn_domain":""},"ssl":{"domain":""},"expose":{"domain":""},"credentials":{"username":"","ttl":86400}}' + echo '{"status":{},"logs":{"lines":50},"test":{"host":""},"start":{},"stop":{},"restart":{},"enable":{},"disable":{},"setup_jitsi":{"jitsi_domain":"","turn_domain":""},"setup_nextcloud":{"turn_domain":"","use_port_443":"yes"},"ssl":{"domain":""},"expose":{"domain":""},"credentials":{"username":"","ttl":86400}}' ;; call) case "$2" in @@ -159,6 +159,27 @@ case "$1" in json_dump ;; + setup_nextcloud) + read -r input + json_load "$input" + json_get_var turn_domain turn_domain "turn.secubox.in" + json_get_var use_port_443 use_port_443 "yes" + + output=$(turnctl setup-nextcloud "$turn_domain" "$use_port_443" 2>&1) + local auth_secret=$(uci_get main.static_auth_secret "") + local tls_port=$(uci_get main.tls_port "443") + local stun_port=$(uci_get main.listening_port "3478") + + json_init + json_add_string result "ok" + json_add_string turn_domain "$turn_domain" + json_add_string auth_secret "$auth_secret" + json_add_int stun_port "$stun_port" + json_add_int tls_port "$tls_port" + json_add_string output "$output" + json_dump + ;; + ssl) read -r input json_load "$input" diff --git a/package/secubox/luci-app-turn/root/usr/share/rpcd/acl.d/luci-app-turn.json b/package/secubox/luci-app-turn/root/usr/share/rpcd/acl.d/luci-app-turn.json index f6bb386c..decb518b 100644 --- a/package/secubox/luci-app-turn/root/usr/share/rpcd/acl.d/luci-app-turn.json +++ b/package/secubox/luci-app-turn/root/usr/share/rpcd/acl.d/luci-app-turn.json @@ -9,7 +9,7 @@ }, "write": { "ubus": { - "luci.turn": ["start", "stop", "restart", "enable", "disable", "setup_jitsi", "ssl", "expose", "credentials"] + "luci.turn": ["start", "stop", "restart", "enable", "disable", "setup_jitsi", "setup_nextcloud", "ssl", "expose", "credentials"] }, "uci": ["turn"] } diff --git a/package/secubox/secubox-app-turn/files/usr/sbin/turnctl b/package/secubox/secubox-app-turn/files/usr/sbin/turnctl index a8fba2b1..cab2493e 100644 --- a/package/secubox/secubox-app-turn/files/usr/sbin/turnctl +++ b/package/secubox/secubox-app-turn/files/usr/sbin/turnctl @@ -118,6 +118,93 @@ cmd_setup_jitsi() { echo "" } +#--- Setup TURN for Nextcloud Talk --- +cmd_setup_nextcloud() { + local turn_domain="${1:-turn.secubox.in}" + local use_port_443="${2:-yes}" + + log "Setting up TURN for Nextcloud Talk..." + + # Enable TURN + uci set turn.main.enabled='1' + uci set turn.main.realm="$turn_domain" + + # Nextcloud recommends port 443 for maximum firewall compatibility + if [ "$use_port_443" = "yes" ] || [ "$use_port_443" = "443" ]; then + uci set turn.main.tls_port='443' + log "Using port 443 for TURN TLS (recommended for Nextcloud)" + fi + + # Auto-detect external IP + local external_ip=$(curl -s -4 https://ifconfig.me 2>/dev/null) + if [ -n "$external_ip" ]; then + uci set turn.main.external_ip="$external_ip" + log "Detected external IP: $external_ip" + fi + + # Generate auth secret if not exists + local auth_secret=$(uci_get main.static_auth_secret "") + if [ -z "$auth_secret" ]; then + auth_secret=$(head -c 32 /dev/urandom | base64 | tr -d '/+=' | head -c 32) + uci set turn.main.static_auth_secret="$auth_secret" + log "Generated new auth secret" + fi + + uci commit turn + + # Setup SSL if using port 443 + if [ "$use_port_443" = "yes" ] || [ "$use_port_443" = "443" ]; then + cmd_ssl "$turn_domain" + fi + + # Start TURN server + /etc/init.d/turn restart + sleep 2 + + # Get configured ports + local stun_port=$(uci_get main.listening_port "3478") + local tls_port=$(uci_get main.tls_port "443") + + log "TURN server configured for Nextcloud Talk!" + echo "" + echo -e "${CYAN}=== Nextcloud Talk Admin Settings ===${NC}" + echo "" + echo "Go to: Settings → Administration → Talk" + echo "" + echo -e "${YELLOW}STUN servers:${NC}" + echo " ${turn_domain}:${stun_port}" + echo "" + echo -e "${YELLOW}TURN server:${NC}" + echo " ${turn_domain}:${tls_port}" + echo "" + echo -e "${YELLOW}TURN secret:${NC}" + echo " ${auth_secret}" + echo "" + echo -e "${YELLOW}Protocol:${NC}" + echo " UDP and TCP" + echo "" + echo -e "${CYAN}=== Important Notes ===${NC}" + echo "" + echo "1. Do NOT add 'turn://' or 'turns://' prefix - just domain:port" + echo "2. Port 443 is recommended for firewall traversal" + echo "3. Both UDP and TCP should be enabled for maximum compatibility" + echo "" + echo -e "${CYAN}=== ICE Server Config (for testing) ===${NC}" + echo "" + echo "{" + echo " \"iceServers\": [" + echo " { \"urls\": \"stun:${turn_domain}:${stun_port}\" }," + echo " {" + echo " \"urls\": [\"turn:${turn_domain}:${tls_port}?transport=tcp\", \"turns:${turn_domain}:${tls_port}?transport=tcp\"]," + echo " \"username\": \"\"," + echo " \"credential\": \"\"" + echo " }" + echo " ]" + echo "}" + echo "" + echo -e "${GREEN}Use 'turnctl credentials [user] [ttl]' to generate time-limited credentials${NC}" +} + #--- Generate Credentials --- cmd_credentials() { local username="${1:-$(date +%s)}" @@ -343,6 +430,8 @@ ${GREEN}Service Commands:${NC} ${GREEN}Setup:${NC} setup-jitsi [domain] [turn-domain] Configure TURN for Jitsi Meet + setup-nextcloud [turn-domain] [port-443] + Configure TURN for Nextcloud Talk ssl [domain] Setup SSL certificate expose [domain] Configure DNS and firewall @@ -354,6 +443,7 @@ ${GREEN}Operations:${NC} ${GREEN}Examples:${NC} turnctl setup-jitsi jitsi.secubox.in turn.secubox.in + turnctl setup-nextcloud turn.secubox.in turnctl ssl turn.secubox.in turnctl credentials webrtc-user 3600 turnctl expose turn.secubox.in @@ -375,6 +465,7 @@ case "$1" in enable) cmd_enable ;; disable) cmd_disable ;; setup-jitsi) shift; cmd_setup_jitsi "$@" ;; + setup-nextcloud) shift; cmd_setup_nextcloud "$@" ;; ssl) shift; cmd_ssl "$@" ;; expose) shift; cmd_expose "$@" ;; credentials) shift; cmd_credentials "$@" ;;