package services import ( "bytes" "context" "log/slog" "os/exec" "strings" ) type VPNService struct{ Config *ConfigService } func NewVPNService(cfg *ConfigService) *VPNService { return &VPNService{ Config: cfg, } } func (v *VPNService) CheckStatus(ctx context.Context) (bool, error) { cmd := exec.CommandContext(ctx, "tailscale", "exit-node", "list") var out bytes.Buffer cmd.Stdout = &out err := cmd.Run() if err != nil { slog.ErrorContext(ctx, "failed to check VPN status", "error", err) return false, err } output := out.String() return strings.Contains(output, "selected"), nil } func (v *VPNService) TurnOn(ctx context.Context) error { selectedServer, _ := v.Config.GetSelectedServer() cmd := exec.CommandContext(ctx, "sudo", "tailscale", "set", "--exit-node="+selectedServer.Host) if err := cmd.Run(); err != nil { slog.ErrorContext(ctx, "failed to turn on VPN", "error", err) return err } return nil } func (v *VPNService) TurnOff(ctx context.Context) error { cmd := exec.CommandContext(ctx, "sudo", "tailscale", "set", "--exit-node=") if err := cmd.Run(); err != nil { slog.ErrorContext(ctx, "failed to turn off VPN", "error", err) return err } return nil } func (v *VPNService) ExitNodeList(ctx context.Context) (string, error) { cmd := exec.CommandContext(ctx, "tailscale", "exit-node", "list") var out bytes.Buffer cmd.Stdout = &out if err := cmd.Run(); err != nil { slog.ErrorContext(ctx, "failed to get exit node list", "error", err) return "", err } return out.String(), nil }