fix(config): preserve WebRTC section in Config.Clone()
Some checks failed
tests / build (push) Failing after 3s

Config.Clone() copied every top-level Data section except WebRTC.
Because api.go receives a clone (not the original), cfg.WebRTC.Enable
was always the zero value at runtime, the subsystem was skipped, and
the WHEP route was never mounted — regardless of CORE_WEBRTC_ENABLE.

Caught on the first live M2 TrueNAS deploy: env said enable=true,
container listened fine, but /api/v3/whep/:id returned Echo's default
JSON 404 (from router) instead of the handler's plain-text
'webrtc: stream not found' (which it would return for an unknown id).

- Add data.WebRTC = d.WebRTC in the struct-copy block.
- Deep-copy NAT1To1IPs alongside the other []string sections.
- Regression test TestConfigCopyWebRTC covers both.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Zac Gaetano 2026-04-17 15:26:11 -04:00
parent d96aa70c27
commit 2d29dc9c4a
2 changed files with 30 additions and 0 deletions

View file

@ -98,6 +98,7 @@ func (d *Config) Clone() *Config {
data.Storage = d.Storage
data.RTMP = d.RTMP
data.SRT = d.SRT
data.WebRTC = d.WebRTC
data.FFmpeg = d.FFmpeg
data.Playout = d.Playout
data.Debug = d.Debug
@ -131,6 +132,8 @@ func (d *Config) Clone() *Config {
data.SRT.Log.Topics = copy.Slice(d.SRT.Log.Topics)
data.WebRTC.NAT1To1IPs = copy.Slice(d.WebRTC.NAT1To1IPs)
data.Router.BlockedPrefixes = copy.Slice(d.Router.BlockedPrefixes)
data.Router.Routes = copy.StringMap(d.Router.Routes)

View file

@ -56,6 +56,33 @@ func TestConfigCopy(t *testing.T) {
require.Equal(t, []string{"foo.com"}, config2.Host.Name)
}
// TestConfigCopyWebRTC is a regression test for Clone() silently dropping the
// WebRTC Data section. The first live M2 deploy surfaced this: env vars bound
// correctly onto the original Config, but Core handed the clone to app/api, so
// cfg.WebRTC.Enable was always the zero value and the subsystem was skipped.
func TestConfigCopyWebRTC(t *testing.T) {
fs, _ := fs.NewMemFilesystem(fs.MemConfig{})
config1 := New(fs)
config1.WebRTC.Enable = true
config1.WebRTC.PublicIP = "10.0.0.25"
config1.WebRTC.NAT1To1IPs = []string{"10.0.0.25", "203.0.113.10"}
config1.WebRTC.UDPMuxPort = 45000
config2 := config1.Clone()
require.Equal(t, true, config2.WebRTC.Enable)
require.Equal(t, "10.0.0.25", config2.WebRTC.PublicIP)
require.Equal(t, []string{"10.0.0.25", "203.0.113.10"}, config2.WebRTC.NAT1To1IPs)
require.Equal(t, 45000, config2.WebRTC.UDPMuxPort)
// NAT1To1IPs is a slice — mutating the clone must not affect the
// source, which is what every other section guarantees via
// copy.Slice. Same contract for WebRTC.
config2.WebRTC.NAT1To1IPs[0] = "mutated"
require.Equal(t, "10.0.0.25", config1.WebRTC.NAT1To1IPs[0])
}
func TestValidateDefault(t *testing.T) {
fs, err := fs.NewMemFilesystem(fs.MemConfig{})
require.NoError(t, err)