Files
Adam Shiervani b3ce335922 feat: add factory reset to replace config-only reset (#529) (#1355)
* fix: implement factory reset replacing config-only reset (#529)

- Add rpcFactoryReset that removes all user data (config, images,
  TLS certs, SSH keys, serial settings, crash dumps) and reboots
- Remove rpcResetConfig RPC handler (keep internal resetConfig for
  OTA and native event use)
- Replace Reset Config UI with Factory Reset button (danger theme)
  and confirmation dialog in Settings > Advanced
- Update localization: add factory reset keys, remove reset config keys
- Add E2E test verifying factory reset UI and dialog copy
- Update ra-all factory reset test to restore SSH keys after reset

* fix: ensure factory reset reboots even when path removal fails

The early return on error exited rpcFactoryReset before reaching the
goroutine that triggers hwReboot, leaving the device partially wiped
with no reboot. Log the warning instead and always fall through to
the reboot.

* fix: remove hardcoded screenshot path from factory reset e2e test
2026-03-27 15:52:52 +01:00

110 lines
3.3 KiB
Go

package kvm
import (
"os"
"time"
"github.com/jetkvm/kvm/internal/diagnostics"
"github.com/jetkvm/kvm/internal/native"
"github.com/jetkvm/kvm/internal/sync"
"github.com/Masterminds/semver/v3"
"github.com/pion/webrtc/v4/pkg/media"
)
var (
nativeInstance native.NativeInterface
nativeCmdLock = sync.Mutex{}
)
func initNative(systemVersion *semver.Version, appVersion *semver.Version) {
if failsafeModeActive {
nativeInstance = &native.EmptyNativeInterface{}
nativeLogger.Warn().Msg("failsafe mode active, using empty native interface")
return
}
nativeLogger.Info().Msg("initializing native proxy")
var err error
nativeInstance, err = native.NewNativeProxy(native.NativeOptions{
SystemVersion: systemVersion,
AppVersion: appVersion,
DisplayRotation: config.GetDisplayRotation(),
DefaultQualityFactor: config.VideoQualityFactor,
MaxRestartAttempts: config.NativeMaxRestart,
OnNativeRestart: func() {
configureDisplayOnNativeRestart()
},
OnVideoStateChange: func(state native.VideoState) {
lastVideoState = state
triggerVideoStateUpdate()
requestDisplayUpdate(false, "video_state_changed")
},
OnIndevEvent: func(event string) {
nativeLogger.Trace().Str("event", event).Msg("indev event received")
wakeDisplay(false, "indev_event")
},
OnRpcEvent: func(event string) {
nativeCmdLock.Lock()
defer nativeCmdLock.Unlock()
nativeLogger.Trace().Str("event", event).Msg("rpc event received")
switch event {
case "resetConfig":
nativeLogger.Info().Msg("Reset configuration request via native rpc event")
err := resetConfig()
if err != nil {
nativeLogger.Warn().Err(err).Msg("error resetting config")
}
_ = rpcReboot(true)
case "reboot":
nativeLogger.Info().Msg("Reboot request via native rpc event")
_ = rpcReboot(true)
case "toggleDHCPClient":
nativeLogger.Info().Msg("Toggle DHCP request via native rpc event")
_ = rpcToggleDHCPClient()
default:
nativeLogger.Warn().Str("event", event).Msg("unknown rpc event received")
}
},
OnVideoFrameReceived: func(frame []byte, duration time.Duration) {
if currentSession != nil {
err := currentSession.VideoTrack.WriteSample(media.Sample{Data: frame, Duration: duration})
if err != nil {
nativeLogger.Warn().Err(err).Msg("error writing sample")
}
}
},
GetSessionInfo: func() diagnostics.SessionInfo {
info := diagnostics.SessionInfo{
ActiveSessions: getActiveSessions(),
HasCurrentSession: currentSession != nil,
}
if currentSession != nil {
sessionInfo := currentSession.GetDiagnosticsInfo()
info.ICEConnectionState = sessionInfo.ICEConnectionState
info.SignalingState = sessionInfo.SignalingState
info.ConnectionState = sessionInfo.ConnectionState
info.DataChannels = sessionInfo.DataChannels
}
return info
},
})
if err != nil {
nativeLogger.Fatal().Err(err).Msg("failed to create native proxy")
}
if err := nativeInstance.Start(); err != nil {
nativeLogger.Fatal().Err(err).Msg("failed to start native proxy")
}
go func() {
if err := nativeInstance.VideoSetEDID(config.EdidString); err != nil {
nativeLogger.Warn().Err(err).Msg("error setting EDID")
}
}()
if os.Getenv("JETKVM_CRASH_TESTING") == "1" {
nativeInstance.DoNotUseThisIsForCrashTestingOnly()
}
}