mirror of
https://github.com/ddworken/hishtory.git
synced 2025-04-01 11:58:38 +02:00
Everything migrated to cobra, but with some very significant TODOs
This commit is contained in:
parent
48e2a41d5c
commit
ecdd22dcdd
108
client/cmd/query.go
Normal file
108
client/cmd/query.go
Normal file
@ -0,0 +1,108 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/ddworken/hishtory/client/hctx"
|
||||
"github.com/ddworken/hishtory/client/lib"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var EXAMPLE_QUERIES string = `Example queries:
|
||||
'hishtory SUBCOMMAND apt-get' # Find shell commands containing 'apt-get'
|
||||
'hishtory SUBCOMMAND apt-get install' # Find shell commands containing 'apt-get' and 'install'
|
||||
'hishtory SUBCOMMAND curl cwd:/tmp/' # Find shell commands containing 'curl' run in '/tmp/'
|
||||
'hishtory SUBCOMMAND curl user:david' # Find shell commands containing 'curl' run by 'david'
|
||||
'hishtory SUBCOMMAND curl host:x1' # Find shell commands containing 'curl' run on 'x1'
|
||||
'hishtory SUBCOMMAND exit_code:1' # Find shell commands that exited with status code 1
|
||||
'hishtory SUBCOMMAND before:2022-02-01' # Find shell commands run before 2022-02-01
|
||||
`
|
||||
|
||||
var queryCmd = &cobra.Command{
|
||||
Use: "query",
|
||||
Short: "Query your shell history",
|
||||
Long: strings.ReplaceAll(EXAMPLE_QUERIES, "SUBCOMMAND", "query"),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ctx := hctx.MakeContext()
|
||||
lib.CheckFatalError(lib.ProcessDeletionRequests(ctx))
|
||||
query(ctx, strings.Join(args, " "))
|
||||
},
|
||||
}
|
||||
|
||||
var tqueryCmd = &cobra.Command{
|
||||
Use: "tquery",
|
||||
Short: "Interactively query your shell history",
|
||||
Long: strings.ReplaceAll(EXAMPLE_QUERIES, "SUBCOMMAND", "tquery"),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ctx := hctx.MakeContext()
|
||||
lib.CheckFatalError(lib.TuiQuery(ctx, strings.Join(args, " ")))
|
||||
},
|
||||
}
|
||||
|
||||
var exportCmd = &cobra.Command{
|
||||
Use: "export",
|
||||
Short: "Export your shell history",
|
||||
Long: strings.ReplaceAll(EXAMPLE_QUERIES, "SUBCOMMAND", "export"),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ctx := hctx.MakeContext()
|
||||
lib.CheckFatalError(lib.ProcessDeletionRequests(ctx))
|
||||
export(ctx, strings.Join(os.Args[2:], " "))
|
||||
},
|
||||
}
|
||||
|
||||
func export(ctx *context.Context, query string) {
|
||||
db := hctx.GetDb(ctx)
|
||||
err := lib.RetrieveAdditionalEntriesFromRemote(ctx)
|
||||
if err != nil {
|
||||
if lib.IsOfflineError(err) {
|
||||
fmt.Println("Warning: hishtory is offline so this may be missing recent results from your other machines!")
|
||||
} else {
|
||||
lib.CheckFatalError(err)
|
||||
}
|
||||
}
|
||||
data, err := lib.Search(ctx, db, query, 0)
|
||||
lib.CheckFatalError(err)
|
||||
for i := len(data) - 1; i >= 0; i-- {
|
||||
fmt.Println(data[i].Command)
|
||||
}
|
||||
}
|
||||
|
||||
func query(ctx *context.Context, query string) {
|
||||
db := hctx.GetDb(ctx)
|
||||
err := lib.RetrieveAdditionalEntriesFromRemote(ctx)
|
||||
if err != nil {
|
||||
if lib.IsOfflineError(err) {
|
||||
fmt.Println("Warning: hishtory is offline so this may be missing recent results from your other machines!")
|
||||
} else {
|
||||
lib.CheckFatalError(err)
|
||||
}
|
||||
}
|
||||
lib.CheckFatalError(displayBannerIfSet(ctx))
|
||||
numResults := 25
|
||||
data, err := lib.Search(ctx, db, query, numResults*5)
|
||||
lib.CheckFatalError(err)
|
||||
lib.CheckFatalError(lib.DisplayResults(ctx, data, numResults))
|
||||
}
|
||||
|
||||
func displayBannerIfSet(ctx *context.Context) error {
|
||||
respBody, err := lib.GetBanner(ctx)
|
||||
if lib.IsOfflineError(err) {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(respBody) > 0 {
|
||||
fmt.Println(string(respBody))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(queryCmd)
|
||||
rootCmd.AddCommand(tqueryCmd)
|
||||
rootCmd.AddCommand(exportCmd)
|
||||
}
|
29
client/cmd/redact.go
Normal file
29
client/cmd/redact.go
Normal file
@ -0,0 +1,29 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/ddworken/hishtory/client/hctx"
|
||||
"github.com/ddworken/hishtory/client/lib"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var force *bool
|
||||
|
||||
var redactCmd = &cobra.Command{
|
||||
Use: "redact",
|
||||
Short: "Query for matching commands and remove them from your shell history",
|
||||
Long: "This removes history entries on the current machine and on all remote machines. Supports the same query format as 'hishtory query'.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ctx := hctx.MakeContext()
|
||||
lib.CheckFatalError(lib.RetrieveAdditionalEntriesFromRemote(ctx))
|
||||
lib.CheckFatalError(lib.ProcessDeletionRequests(ctx))
|
||||
query := strings.Join(args, " ")
|
||||
lib.CheckFatalError(lib.Redact(ctx, query, *force))
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(redactCmd)
|
||||
force = redactCmd.Flags().Bool("force", false, "Force redaction with no confirmation prompting")
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
/*
|
||||
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||
|
||||
*/
|
||||
package cmd
|
||||
|
||||
@ -10,21 +9,10 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
||||
|
||||
// rootCmd represents the base command when called without any subcommands
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "client",
|
||||
Short: "A brief description of your application",
|
||||
Long: `A longer description that spans multiple lines and likely contains
|
||||
examples and usage of using your application. For example:
|
||||
|
||||
Cobra is a CLI library for Go that empowers applications.
|
||||
This application is a tool to generate the needed files
|
||||
to quickly create a Cobra application.`,
|
||||
// Uncomment the following line if your bare application
|
||||
// has an action associated with it:
|
||||
// Run: func(cmd *cobra.Command, args []string) { },
|
||||
Use: "hiSHtory",
|
||||
Short: "A better shell history",
|
||||
}
|
||||
|
||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||
@ -36,16 +24,4 @@ func Execute() {
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Here you will define your flags and configuration settings.
|
||||
// Cobra supports persistent flags, which, if defined here,
|
||||
// will be global for your application.
|
||||
|
||||
// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.client.yaml)")
|
||||
|
||||
// Cobra also supports local flags, which will only run
|
||||
// when this action is called directly.
|
||||
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
}
|
||||
|
||||
|
||||
func init() {}
|
||||
|
138
client/cmd/saveHistoryEntry.go
Normal file
138
client/cmd/saveHistoryEntry.go
Normal file
@ -0,0 +1,138 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/ddworken/hishtory/client/data"
|
||||
"github.com/ddworken/hishtory/client/hctx"
|
||||
"github.com/ddworken/hishtory/client/lib"
|
||||
"github.com/ddworken/hishtory/shared"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var saveHistoryEntryCmd = &cobra.Command{
|
||||
Use: "saveHistoryEntry",
|
||||
// TODO: hide this from the help info?
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ctx := hctx.MakeContext()
|
||||
lib.CheckFatalError(maybeUploadSkippedHistoryEntries(ctx))
|
||||
saveHistoryEntry(ctx)
|
||||
},
|
||||
}
|
||||
|
||||
func maybeUploadSkippedHistoryEntries(ctx *context.Context) error {
|
||||
config := hctx.GetConf(ctx)
|
||||
if !config.HaveMissedUploads {
|
||||
return nil
|
||||
}
|
||||
if config.IsOffline {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Upload the missing entries
|
||||
db := hctx.GetDb(ctx)
|
||||
query := fmt.Sprintf("after:%s", time.Unix(config.MissedUploadTimestamp, 0).Format("2006-01-02"))
|
||||
entries, err := lib.Search(ctx, db, query, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retrieve history entries that haven't been uploaded yet: %v", err)
|
||||
}
|
||||
hctx.GetLogger().Infof("Uploading %d history entries that previously failed to upload (query=%#v)\n", len(entries), query)
|
||||
jsonValue, err := lib.EncryptAndMarshal(config, entries)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = lib.ApiPost("/api/v1/submit?source_device_id="+config.DeviceId, "application/json", jsonValue)
|
||||
if err != nil {
|
||||
// Failed to upload the history entry, so we must still be offline. So just return nil and we'll try again later.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Mark down that we persisted it
|
||||
config.HaveMissedUploads = false
|
||||
config.MissedUploadTimestamp = 0
|
||||
err = hctx.SetConfig(config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to mark a history entry as uploaded: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func saveHistoryEntry(ctx *context.Context) {
|
||||
config := hctx.GetConf(ctx)
|
||||
if !config.IsEnabled {
|
||||
hctx.GetLogger().Infof("Skipping saving a history entry because hishtory is disabled\n")
|
||||
return
|
||||
}
|
||||
entry, err := lib.BuildHistoryEntry(ctx, os.Args)
|
||||
lib.CheckFatalError(err)
|
||||
if entry == nil {
|
||||
hctx.GetLogger().Infof("Skipping saving a history entry because we did not build a history entry (was the command prefixed with a space and/or empty?)\n")
|
||||
return
|
||||
}
|
||||
|
||||
// Persist it locally
|
||||
db := hctx.GetDb(ctx)
|
||||
err = lib.ReliableDbCreate(db, *entry)
|
||||
lib.CheckFatalError(err)
|
||||
|
||||
// Persist it remotely
|
||||
if !config.IsOffline {
|
||||
jsonValue, err := lib.EncryptAndMarshal(config, []*data.HistoryEntry{entry})
|
||||
lib.CheckFatalError(err)
|
||||
_, err = lib.ApiPost("/api/v1/submit?source_device_id="+config.DeviceId, "application/json", jsonValue)
|
||||
if err != nil {
|
||||
if lib.IsOfflineError(err) {
|
||||
hctx.GetLogger().Infof("Failed to remotely persist hishtory entry because we failed to connect to the remote server! This is likely because the device is offline, but also could be because the remote server is having reliability issues. Original error: %v", err)
|
||||
if !config.HaveMissedUploads {
|
||||
config.HaveMissedUploads = true
|
||||
config.MissedUploadTimestamp = time.Now().Unix()
|
||||
lib.CheckFatalError(hctx.SetConfig(config))
|
||||
}
|
||||
} else {
|
||||
lib.CheckFatalError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there is a pending dump request and reply to it if so
|
||||
dumpRequests, err := lib.GetDumpRequests(config)
|
||||
if err != nil {
|
||||
if lib.IsOfflineError(err) {
|
||||
// It is fine to just ignore this, the next command will retry the API and eventually we will respond to any pending dump requests
|
||||
dumpRequests = []*shared.DumpRequest{}
|
||||
hctx.GetLogger().Infof("Failed to check for dump requests because we failed to connect to the remote server!")
|
||||
} else {
|
||||
lib.CheckFatalError(err)
|
||||
}
|
||||
}
|
||||
if len(dumpRequests) > 0 {
|
||||
lib.CheckFatalError(lib.RetrieveAdditionalEntriesFromRemote(ctx))
|
||||
entries, err := lib.Search(ctx, db, "", 0)
|
||||
lib.CheckFatalError(err)
|
||||
var encEntries []*shared.EncHistoryEntry
|
||||
for _, entry := range entries {
|
||||
enc, err := data.EncryptHistoryEntry(config.UserSecret, *entry)
|
||||
lib.CheckFatalError(err)
|
||||
encEntries = append(encEntries, &enc)
|
||||
}
|
||||
reqBody, err := json.Marshal(encEntries)
|
||||
lib.CheckFatalError(err)
|
||||
for _, dumpRequest := range dumpRequests {
|
||||
if !config.IsOffline {
|
||||
_, err := lib.ApiPost("/api/v1/submit-dump?user_id="+dumpRequest.UserId+"&requesting_device_id="+dumpRequest.RequestingDeviceId+"&source_device_id="+config.DeviceId, "application/json", reqBody)
|
||||
lib.CheckFatalError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle deletion requests
|
||||
lib.CheckFatalError(lib.ProcessDeletionRequests(ctx))
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(saveHistoryEntryCmd)
|
||||
}
|
@ -13,7 +13,7 @@ var verbose *bool
|
||||
|
||||
var statusCmd = &cobra.Command{
|
||||
Use: "status",
|
||||
Short: "Get the hishtory status",
|
||||
Short: "View status info including the secret key which is needed to sync shell history from another machine",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ctx := hctx.MakeContext()
|
||||
config := hctx.GetConf(ctx)
|
||||
|
269
hishtory.go
269
hishtory.go
@ -1,271 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ddworken/hishtory/client/cmd"
|
||||
"github.com/ddworken/hishtory/client/data"
|
||||
"github.com/ddworken/hishtory/client/hctx"
|
||||
"github.com/ddworken/hishtory/client/lib"
|
||||
"github.com/ddworken/hishtory/shared"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) == 1 {
|
||||
fmt.Println("Must specify a command! Do you mean `hishtory query`?")
|
||||
return
|
||||
}
|
||||
switch os.Args[1] {
|
||||
case "saveHistoryEntry":
|
||||
ctx := hctx.MakeContext()
|
||||
lib.CheckFatalError(maybeUploadSkippedHistoryEntries(ctx))
|
||||
saveHistoryEntry(ctx)
|
||||
case "query":
|
||||
ctx := hctx.MakeContext()
|
||||
lib.CheckFatalError(lib.ProcessDeletionRequests(ctx))
|
||||
query(ctx, strings.Join(os.Args[2:], " "))
|
||||
case "tquery":
|
||||
ctx := hctx.MakeContext()
|
||||
lib.CheckFatalError(lib.TuiQuery(ctx, strings.Join(os.Args[2:], " ")))
|
||||
case "export":
|
||||
ctx := hctx.MakeContext()
|
||||
lib.CheckFatalError(lib.ProcessDeletionRequests(ctx))
|
||||
export(ctx, strings.Join(os.Args[2:], " "))
|
||||
case "redact":
|
||||
fallthrough
|
||||
case "delete":
|
||||
ctx := hctx.MakeContext()
|
||||
lib.CheckFatalError(lib.RetrieveAdditionalEntriesFromRemote(ctx))
|
||||
lib.CheckFatalError(lib.ProcessDeletionRequests(ctx))
|
||||
query := strings.Join(os.Args[2:], " ")
|
||||
force := false
|
||||
if os.Args[2] == "--force" {
|
||||
query = strings.Join(os.Args[3:], " ")
|
||||
force = true
|
||||
}
|
||||
lib.CheckFatalError(lib.Redact(ctx, query, force))
|
||||
case "init":
|
||||
fallthrough
|
||||
case "install":
|
||||
fallthrough
|
||||
case "uninstall":
|
||||
fallthrough
|
||||
case "import":
|
||||
fallthrough
|
||||
case "enable":
|
||||
fallthrough
|
||||
case "disable":
|
||||
fallthrough
|
||||
case "version":
|
||||
fallthrough
|
||||
case "status":
|
||||
fallthrough
|
||||
case "update":
|
||||
fallthrough
|
||||
case "config-set":
|
||||
fallthrough
|
||||
case "config-get":
|
||||
fallthrough
|
||||
case "config-add":
|
||||
fallthrough
|
||||
case "reupload":
|
||||
fallthrough
|
||||
case "config-delete":
|
||||
cmd.Execute()
|
||||
case "-h":
|
||||
fallthrough
|
||||
case "help":
|
||||
fmt.Print(`hiSHtory: Better shell history
|
||||
|
||||
Supported commands:
|
||||
'hishtory query': Query for matching commands and display them in a table. Examples:
|
||||
'hishtory query apt-get' # Find shell commands containing 'apt-get'
|
||||
'hishtory query apt-get install' # Find shell commands containing 'apt-get' and 'install'
|
||||
'hishtory query curl cwd:/tmp/' # Find shell commands containing 'curl' run in '/tmp/'
|
||||
'hishtory query curl user:david' # Find shell commands containing 'curl' run by 'david'
|
||||
'hishtory query curl host:x1' # Find shell commands containing 'curl' run on 'x1'
|
||||
'hishtory query exit_code:1' # Find shell commands that exited with status code 1
|
||||
'hishtory query before:2022-02-01' # Find shell commands run before 2022-02-01
|
||||
'hishtory export': Query for matching commands and display them in list without any other
|
||||
metadata. Supports the same query format as 'hishtory query'.
|
||||
'hishtory redact': Query for matching commands and remove them from your shell history (on the
|
||||
current machine and on all remote machines). Supports the same query format as 'hishtory query'.
|
||||
'hishtory update': Securely update hishtory to the latest version.
|
||||
'hishtory disable': Stop recording shell commands
|
||||
'hishtory enable': Start recording shell commands
|
||||
'hishtory status': View status info including the secret key which is needed to sync shell
|
||||
history from another machine.
|
||||
'hishtory init': Set the secret key to enable syncing shell commands from another
|
||||
machine with a matching secret key.
|
||||
'hishtory config-get', 'hishtory config-set', 'hishtory config-add', 'hishtory config-delete': Edit the config. See the README for details on each of the config options.
|
||||
'hishtory uninstall': Permanently uninstall hishtory
|
||||
'hishtory help': View this help page
|
||||
`)
|
||||
default:
|
||||
lib.CheckFatalError(fmt.Errorf("unknown command: %s", os.Args[1]))
|
||||
}
|
||||
}
|
||||
|
||||
func query(ctx *context.Context, query string) {
|
||||
db := hctx.GetDb(ctx)
|
||||
err := lib.RetrieveAdditionalEntriesFromRemote(ctx)
|
||||
if err != nil {
|
||||
if lib.IsOfflineError(err) {
|
||||
fmt.Println("Warning: hishtory is offline so this may be missing recent results from your other machines!")
|
||||
} else {
|
||||
lib.CheckFatalError(err)
|
||||
}
|
||||
}
|
||||
lib.CheckFatalError(displayBannerIfSet(ctx))
|
||||
numResults := 25
|
||||
data, err := lib.Search(ctx, db, query, numResults*5)
|
||||
lib.CheckFatalError(err)
|
||||
lib.CheckFatalError(lib.DisplayResults(ctx, data, numResults))
|
||||
}
|
||||
|
||||
func displayBannerIfSet(ctx *context.Context) error {
|
||||
respBody, err := lib.GetBanner(ctx)
|
||||
if lib.IsOfflineError(err) {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(respBody) > 0 {
|
||||
fmt.Println(string(respBody))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func maybeUploadSkippedHistoryEntries(ctx *context.Context) error {
|
||||
config := hctx.GetConf(ctx)
|
||||
if !config.HaveMissedUploads {
|
||||
return nil
|
||||
}
|
||||
if config.IsOffline {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Upload the missing entries
|
||||
db := hctx.GetDb(ctx)
|
||||
query := fmt.Sprintf("after:%s", time.Unix(config.MissedUploadTimestamp, 0).Format("2006-01-02"))
|
||||
entries, err := lib.Search(ctx, db, query, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retrieve history entries that haven't been uploaded yet: %v", err)
|
||||
}
|
||||
hctx.GetLogger().Infof("Uploading %d history entries that previously failed to upload (query=%#v)\n", len(entries), query)
|
||||
jsonValue, err := lib.EncryptAndMarshal(config, entries)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = lib.ApiPost("/api/v1/submit?source_device_id="+config.DeviceId, "application/json", jsonValue)
|
||||
if err != nil {
|
||||
// Failed to upload the history entry, so we must still be offline. So just return nil and we'll try again later.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Mark down that we persisted it
|
||||
config.HaveMissedUploads = false
|
||||
config.MissedUploadTimestamp = 0
|
||||
err = hctx.SetConfig(config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to mark a history entry as uploaded: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func saveHistoryEntry(ctx *context.Context) {
|
||||
config := hctx.GetConf(ctx)
|
||||
if !config.IsEnabled {
|
||||
hctx.GetLogger().Infof("Skipping saving a history entry because hishtory is disabled\n")
|
||||
return
|
||||
}
|
||||
entry, err := lib.BuildHistoryEntry(ctx, os.Args)
|
||||
lib.CheckFatalError(err)
|
||||
if entry == nil {
|
||||
hctx.GetLogger().Infof("Skipping saving a history entry because we did not build a history entry (was the command prefixed with a space and/or empty?)\n")
|
||||
return
|
||||
}
|
||||
|
||||
// Persist it locally
|
||||
db := hctx.GetDb(ctx)
|
||||
err = lib.ReliableDbCreate(db, *entry)
|
||||
lib.CheckFatalError(err)
|
||||
|
||||
// Persist it remotely
|
||||
if !config.IsOffline {
|
||||
jsonValue, err := lib.EncryptAndMarshal(config, []*data.HistoryEntry{entry})
|
||||
lib.CheckFatalError(err)
|
||||
_, err = lib.ApiPost("/api/v1/submit?source_device_id="+config.DeviceId, "application/json", jsonValue)
|
||||
if err != nil {
|
||||
if lib.IsOfflineError(err) {
|
||||
hctx.GetLogger().Infof("Failed to remotely persist hishtory entry because we failed to connect to the remote server! This is likely because the device is offline, but also could be because the remote server is having reliability issues. Original error: %v", err)
|
||||
if !config.HaveMissedUploads {
|
||||
config.HaveMissedUploads = true
|
||||
config.MissedUploadTimestamp = time.Now().Unix()
|
||||
lib.CheckFatalError(hctx.SetConfig(config))
|
||||
}
|
||||
} else {
|
||||
lib.CheckFatalError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there is a pending dump request and reply to it if so
|
||||
dumpRequests, err := lib.GetDumpRequests(config)
|
||||
if err != nil {
|
||||
if lib.IsOfflineError(err) {
|
||||
// It is fine to just ignore this, the next command will retry the API and eventually we will respond to any pending dump requests
|
||||
dumpRequests = []*shared.DumpRequest{}
|
||||
hctx.GetLogger().Infof("Failed to check for dump requests because we failed to connect to the remote server!")
|
||||
} else {
|
||||
lib.CheckFatalError(err)
|
||||
}
|
||||
}
|
||||
if len(dumpRequests) > 0 {
|
||||
lib.CheckFatalError(lib.RetrieveAdditionalEntriesFromRemote(ctx))
|
||||
entries, err := lib.Search(ctx, db, "", 0)
|
||||
lib.CheckFatalError(err)
|
||||
var encEntries []*shared.EncHistoryEntry
|
||||
for _, entry := range entries {
|
||||
enc, err := data.EncryptHistoryEntry(config.UserSecret, *entry)
|
||||
lib.CheckFatalError(err)
|
||||
encEntries = append(encEntries, &enc)
|
||||
}
|
||||
reqBody, err := json.Marshal(encEntries)
|
||||
lib.CheckFatalError(err)
|
||||
for _, dumpRequest := range dumpRequests {
|
||||
if !config.IsOffline {
|
||||
_, err := lib.ApiPost("/api/v1/submit-dump?user_id="+dumpRequest.UserId+"&requesting_device_id="+dumpRequest.RequestingDeviceId+"&source_device_id="+config.DeviceId, "application/json", reqBody)
|
||||
lib.CheckFatalError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle deletion requests
|
||||
lib.CheckFatalError(lib.ProcessDeletionRequests(ctx))
|
||||
}
|
||||
|
||||
func export(ctx *context.Context, query string) {
|
||||
db := hctx.GetDb(ctx)
|
||||
err := lib.RetrieveAdditionalEntriesFromRemote(ctx)
|
||||
if err != nil {
|
||||
if lib.IsOfflineError(err) {
|
||||
fmt.Println("Warning: hishtory is offline so this may be missing recent results from your other machines!")
|
||||
} else {
|
||||
lib.CheckFatalError(err)
|
||||
}
|
||||
}
|
||||
data, err := lib.Search(ctx, db, query, 0)
|
||||
lib.CheckFatalError(err)
|
||||
for i := len(data) - 1; i >= 0; i-- {
|
||||
fmt.Println(data[i].Command)
|
||||
}
|
||||
cmd.Execute()
|
||||
}
|
||||
|
||||
// TODO(feature): Add a session_id column that corresponds to the shell session the command was run in
|
||||
|
||||
/*
|
||||
Remaining things:
|
||||
* Support exclusions in searches
|
||||
* Figure out how to hide certain things from the help doc
|
||||
* Figure out how to reorder the docs
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user