feat: complete Windows client control and recovery paths
This commit is contained in:
@@ -9,8 +9,10 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/rvbox/rvbox/internal/client/windowsservice"
|
||||
"github.com/rvbox/rvbox/internal/client/windowstray"
|
||||
"golang.org/x/sys/windows/svc"
|
||||
)
|
||||
|
||||
@@ -34,9 +36,80 @@ func runService(configPath string, diagnostics io.Writer) error {
|
||||
}
|
||||
|
||||
func runWindowsService(configPath string, diagnostics io.Writer) error {
|
||||
return windowsservice.Run(func(ctx context.Context) error { return runClientDaemon(ctx, configPath, diagnostics) })
|
||||
return windowsservice.Run(func(ctx context.Context) error {
|
||||
// The tray endpoint lives in the same LocalSystem service process. It
|
||||
// has no store access; the handler below returns only bounded status/path
|
||||
// data and rechecks SCM authorization in the native pipe adapter.
|
||||
go func() {
|
||||
if err := windowstray.Serve(ctx, func(requestContext context.Context, _ windowstray.Peer, request windowstray.Frame) (windowstray.Frame, error) {
|
||||
return handleTrayRequest(requestContext, configPath, request)
|
||||
}); err != nil && ctx.Err() == nil && diagnostics != nil {
|
||||
_, _ = fmt.Fprintf(diagnostics, "rvbox tray endpoint stopped: %v\n", err)
|
||||
}
|
||||
}()
|
||||
return runClientDaemon(ctx, configPath, diagnostics)
|
||||
})
|
||||
}
|
||||
|
||||
func runTray(configPath string, diagnostics io.Writer) error {
|
||||
return errors.New("Windows tray frontend is not available in this build")
|
||||
_ = configPath // the tray obtains the canonical paths from the service.
|
||||
return windowstray.Run(context.Background(), diagnostics)
|
||||
}
|
||||
|
||||
func handleTrayRequest(ctx context.Context, configPath string, request windowstray.Frame) (windowstray.Frame, error) {
|
||||
response := windowstray.Frame{Action: windowstray.ActionStatus}
|
||||
switch request.Action {
|
||||
case windowstray.ActionStatus:
|
||||
response.Payload = []byte("RVBox service=running")
|
||||
case windowstray.ActionOpenConfig:
|
||||
response.Payload = []byte(configPath)
|
||||
case windowstray.ActionOpenLog:
|
||||
configured, err := loadClientConfig(configPath)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
logPath := configured.Observability.LogFile
|
||||
if logPath == "" {
|
||||
logPath = filepath.Join(filepath.Dir(configPath), "logs", "rvbox.log")
|
||||
}
|
||||
response.Payload = []byte(logPath)
|
||||
case windowstray.ActionStartService:
|
||||
if err := windowsservice.Start(); err != nil {
|
||||
return response, err
|
||||
}
|
||||
response.Payload = []byte("RVBox service start requested")
|
||||
case windowstray.ActionStopService:
|
||||
// A service cannot synchronously wait for its own stop request from the
|
||||
// pipe handler: the SCM callback must return so the process can unwind.
|
||||
go func() { _ = windowsservice.Stop(30) }()
|
||||
response.Payload = []byte("RVBox service stop requested")
|
||||
case windowstray.ActionRestartService:
|
||||
// Restart must be performed by the external UAC helper. If the tray is
|
||||
// already elevated, its native fallback still launches the canonical
|
||||
// --restart-service mode outside this service process.
|
||||
return response, errors.New("restart requires the external service helper")
|
||||
case windowstray.ActionSetAutomatic:
|
||||
if err := windowsservice.Configure(windowsservice.StartupAutomatic); err != nil {
|
||||
return response, err
|
||||
}
|
||||
response.Payload = []byte("RVBox service startup set to automatic")
|
||||
case windowstray.ActionSetManual:
|
||||
if err := windowsservice.Configure(windowsservice.StartupManual); err != nil {
|
||||
return response, err
|
||||
}
|
||||
response.Payload = []byte("RVBox service startup set to manual")
|
||||
case windowstray.ActionExitTray:
|
||||
response.Payload = []byte("tray exit acknowledged")
|
||||
default:
|
||||
return response, fmt.Errorf("unknown tray action %d", request.Action)
|
||||
}
|
||||
if len(response.Payload) > 4<<10 {
|
||||
response.Payload = []byte(strings.TrimSpace(string(response.Payload[:4<<10])))
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return response, ctx.Err()
|
||||
default:
|
||||
return response, nil
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user