Add support for enabling/disabling syncing post-install

This commit is contained in:
David Dworken 2024-04-28 16:33:43 -07:00
parent f678a4ffba
commit 10f01b8483
No known key found for this signature in database
6 changed files with 160 additions and 3 deletions

View File

@ -155,7 +155,7 @@ If you don't need the ability to sync your shell history, you can install hiSHto
curl https://hishtory.dev/install.py | HISHTORY_OFFLINE=true python3 -
```
This disables syncing and it is not possible to re-enable syncing after doing this.
This disables syncing completely so that the client will not rely on the hiSHtory backend at all. You can also change the syncing status via `hishtory syncing enable` or `hishtory syncing disable`.
</blockquote></details>

View File

@ -3127,4 +3127,66 @@ func TestForceInit(t *testing.T) {
require.NotContains(t, tester.RunInteractiveShell(t, `hishtory export`), "echo foobar")
}
func TestChangeSyncingStatus(t *testing.T) {
markTestForSharding(t, 14)
defer testutils.BackupAndRestore(t)()
tester := zshTester{}
// Install it offline and record a command or two
userSecret := installWithOnlineStatus(t, tester, Offline)
assertOnlineStatus(t, Offline)
tester.RunInteractiveShell(t, `echo "device1_whileOffline_1"`)
testutils.CompareGoldens(t,
tester.RunInteractiveShell(t, `hishtory status -v | grep -v User | grep -v Device | grep -v Secret`),
"TestChangeSyncingStatus-Offline",
)
// Go online
out := tester.RunInteractiveShell(t, `hishtory syncing enable`)
require.Equal(t, "Enabled syncing successfully\n", out)
testutils.CompareGoldens(t,
tester.RunInteractiveShell(t, `hishtory status -v | grep -v User | grep -v Device | grep -v Secret`),
"TestChangeSyncingStatus-Online",
)
// Back up that device and set up another device to confirm syncing is working
restoreDev1 := testutils.BackupAndRestoreWithId(t, "dev1")
installHishtory(t, tester, userSecret)
out = tester.RunInteractiveShell(t, `hishtory export`)
require.Contains(t, out, "device1_whileOffline_1")
testutils.CompareGoldens(t,
tester.RunInteractiveShell(t, `hishtory status -v | grep -v User | grep -v Device | grep -v Secret`),
"TestChangeSyncingStatus-Online",
)
// Go back to the first device, disable syncing, and then record a command
restoreDev2 := testutils.BackupAndRestoreWithId(t, "dev2")
restoreDev1()
testutils.CompareGoldens(t,
tester.RunInteractiveShell(t, `hishtory status -v | grep -v User | grep -v Device | grep -v Secret`),
"TestChangeSyncingStatus-Online",
)
out = tester.RunInteractiveShell(t, `hishtory syncing disable`)
testutils.CompareGoldens(t,
tester.RunInteractiveShell(t, `hishtory status -v | grep -v User | grep -v Device | grep -v Secret`),
"TestChangeSyncingStatus-Offline",
)
require.Equal(t, "Disabled syncing successfully\n", out)
tester.RunInteractiveShell(t, `echo "device1_whileOffline_2"`)
out = tester.RunInteractiveShell(t, `hishtory export`)
require.Contains(t, out, "device1_whileOffline_1")
require.Contains(t, out, "device1_whileOffline_2")
// Then go back to the second device which won't see that command
testutils.BackupAndRestoreWithId(t, "dev1")
restoreDev2()
out = tester.RunInteractiveShell(t, `hishtory export`)
require.Contains(t, out, "device1_whileOffline_1")
require.NotContains(t, out, "device1_whileOffline_2")
testutils.CompareGoldens(t,
tester.RunInteractiveShell(t, `hishtory status -v | grep -v User | grep -v Device | grep -v Secret`),
"TestChangeSyncingStatus-Online",
)
}
// TODO: somehow test/confirm that hishtory works even if only bash/only zsh is installed

View File

@ -599,12 +599,15 @@ func setup(userSecret string, isOffline bool) error {
if config.IsOffline {
return nil
}
ctx := hctx.MakeContext()
return registerAndBootstrapDevice(hctx.MakeContext(), &config, db, userSecret)
}
func registerAndBootstrapDevice(ctx context.Context, config *hctx.ClientConfig, db *gorm.DB, userSecret string) error {
registerPath := "/api/v1/register?user_id=" + data.UserId(userSecret) + "&device_id=" + config.DeviceId
if isIntegrationTestDevice() {
registerPath += "&is_integration_test_device=true"
}
_, err = lib.ApiGet(ctx, registerPath)
_, err := lib.ApiGet(ctx, registerPath)
if err != nil {
return fmt.Errorf("failed to register device with backend: %w", err)
}

82
client/cmd/syncing.go Normal file
View File

@ -0,0 +1,82 @@
package cmd
import (
"context"
"fmt"
"github.com/ddworken/hishtory/client/data"
"github.com/ddworken/hishtory/client/hctx"
"github.com/ddworken/hishtory/client/lib"
"github.com/spf13/cobra"
)
var syncingCmd = &cobra.Command{
Use: "syncing",
Short: "Configure syncing to enable or disable syncing with the hishtory backend",
ValidArgs: []string{"disable", "enable"},
Args: cobra.MatchAll(cobra.OnlyValidArgs, cobra.ExactArgs(1)),
Run: func(cmd *cobra.Command, args []string) {
syncingStatus := false
if args[0] == "disable" {
syncingStatus = false
} else if args[0] == "enable" {
syncingStatus = true
} else {
lib.CheckFatalError(fmt.Errorf("unexpected syncing argument %q", args[0]))
}
ctx := hctx.MakeContext()
conf := hctx.GetConf(ctx)
if syncingStatus {
if conf.IsOffline {
lib.CheckFatalError(switchToOnline(ctx))
fmt.Println("Enabled syncing successfully")
} else {
lib.CheckFatalError(fmt.Errorf("device is already online"))
}
} else {
if conf.IsOffline {
lib.CheckFatalError(fmt.Errorf("device is already offline"))
} else {
lib.CheckFatalError(switchToOffline(ctx))
fmt.Println("Disabled syncing successfully")
}
}
},
}
func switchToOnline(ctx context.Context) error {
config := hctx.GetConf(ctx)
config.IsOffline = false
err := hctx.SetConfig(config)
if err != nil {
return fmt.Errorf("failed to switch device to online due to error while setting config: %w", err)
}
err = registerAndBootstrapDevice(ctx, config, hctx.GetDb(ctx), config.UserSecret)
if err != nil {
return fmt.Errorf("failed to register device with backend: %w", err)
}
err = lib.Reupload(ctx)
if err != nil {
return fmt.Errorf("failed to switch device to online due to error while uploading history entries: %w", err)
}
return nil
}
func switchToOffline(ctx context.Context) error {
config := hctx.GetConf(ctx)
config.IsOffline = true
err := hctx.SetConfig(config)
if err != nil {
return fmt.Errorf("failed to switch device to offline due to error while setting config: %w", err)
}
_, err = lib.ApiPost(ctx, "/api/v1/uninstall?user_id="+data.UserId(hctx.GetConf(ctx).UserSecret)+"&device_id="+hctx.GetConf(ctx).DeviceId, "application/json", []byte{})
if err != nil {
return fmt.Errorf("failed to switch device to offline due to error while deleting sync state: %w", err)
}
return nil
}
func init() {
rootCmd.AddCommand(syncingCmd)
}

View File

@ -0,0 +1,4 @@
hiSHtory: v0.Unknown
Enabled: true
Sync Mode: Disabled
Commit Hash: Unknown

View File

@ -0,0 +1,6 @@
hiSHtory: v0.Unknown
Enabled: true
Sync Mode: Enabled
Sync Server: http://localhost:8080
Sync Status: Synced
Commit Hash: Unknown