mirror of
https://github.com/ddworken/hishtory.git
synced 2025-06-20 03:47:54 +02:00
commit
02d04928d5
@ -196,7 +196,7 @@ func apiSubmitHandler(ctx context.Context, w http.ResponseWriter, r *http.Reques
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("failed to execute transaction to add entries to DB: %v", err))
|
panic(fmt.Errorf("failed to execute transaction to add entries to DB: %w", err))
|
||||||
}
|
}
|
||||||
if GLOBAL_STATSD != nil {
|
if GLOBAL_STATSD != nil {
|
||||||
GLOBAL_STATSD.Count("hishtory.submit", int64(len(devices)), []string{}, 1.0)
|
GLOBAL_STATSD.Count("hishtory.submit", int64(len(devices)), []string{}, 1.0)
|
||||||
@ -312,7 +312,7 @@ func apiGetPendingDumpRequestsHandler(ctx context.Context, w http.ResponseWriter
|
|||||||
checkGormResult(GLOBAL_DB.WithContext(ctx).Where("user_id = ? AND requesting_device_id != ?", userId, deviceId).Find(&dumpRequests))
|
checkGormResult(GLOBAL_DB.WithContext(ctx).Where("user_id = ? AND requesting_device_id != ?", userId, deviceId).Find(&dumpRequests))
|
||||||
respBody, err := json.Marshal(dumpRequests)
|
respBody, err := json.Marshal(dumpRequests)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("failed to JSON marshall the dump requests: %v", err))
|
panic(fmt.Errorf("failed to JSON marshall the dump requests: %w", err))
|
||||||
}
|
}
|
||||||
w.Write(respBody)
|
w.Write(respBody)
|
||||||
}
|
}
|
||||||
@ -342,7 +342,7 @@ func apiSubmitDumpHandler(ctx context.Context, w http.ResponseWriter, r *http.Re
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("failed to execute transaction to add dumped DB: %v", err))
|
panic(fmt.Errorf("failed to execute transaction to add dumped DB: %w", err))
|
||||||
}
|
}
|
||||||
checkGormResult(GLOBAL_DB.WithContext(ctx).Delete(&shared.DumpRequest{}, "user_id = ? AND requesting_device_id = ?", userId, requestingDeviceId))
|
checkGormResult(GLOBAL_DB.WithContext(ctx).Delete(&shared.DumpRequest{}, "user_id = ? AND requesting_device_id = ?", userId, requestingDeviceId))
|
||||||
updateUsageData(ctx, r, userId, srcDeviceId, len(entries), false)
|
updateUsageData(ctx, r, userId, srcDeviceId, len(entries), false)
|
||||||
@ -372,7 +372,7 @@ func getDeletionRequestsHandler(ctx context.Context, w http.ResponseWriter, r *h
|
|||||||
checkGormResult(GLOBAL_DB.WithContext(ctx).Where("user_id = ? AND destination_device_id = ?", userId, deviceId).Find(&deletionRequests))
|
checkGormResult(GLOBAL_DB.WithContext(ctx).Where("user_id = ? AND destination_device_id = ?", userId, deviceId).Find(&deletionRequests))
|
||||||
respBody, err := json.Marshal(deletionRequests)
|
respBody, err := json.Marshal(deletionRequests)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("failed to JSON marshall the dump requests: %v", err))
|
panic(fmt.Errorf("failed to JSON marshall the dump requests: %w", err))
|
||||||
}
|
}
|
||||||
w.Write(respBody)
|
w.Write(respBody)
|
||||||
}
|
}
|
||||||
@ -491,11 +491,11 @@ func OpenDB() (*gorm.DB, error) {
|
|||||||
if isTestEnvironment() {
|
if isTestEnvironment() {
|
||||||
db, err := gorm.Open(sqlite.Open("file::memory:?_journal_mode=WAL&cache=shared"), &gorm.Config{})
|
db, err := gorm.Open(sqlite.Open("file::memory:?_journal_mode=WAL&cache=shared"), &gorm.Config{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to connect to the DB: %v", err)
|
return nil, fmt.Errorf("failed to connect to the DB: %w", err)
|
||||||
}
|
}
|
||||||
underlyingDb, err := db.DB()
|
underlyingDb, err := db.DB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to access underlying DB: %v", err)
|
return nil, fmt.Errorf("failed to access underlying DB: %w", err)
|
||||||
}
|
}
|
||||||
underlyingDb.SetMaxOpenConns(1)
|
underlyingDb.SetMaxOpenConns(1)
|
||||||
db.Exec("PRAGMA journal_mode = WAL")
|
db.Exec("PRAGMA journal_mode = WAL")
|
||||||
@ -521,7 +521,7 @@ func OpenDB() (*gorm.DB, error) {
|
|||||||
var err error
|
var err error
|
||||||
db, err = gorm.Open(sqlite.Open(sqliteDb), &gorm.Config{Logger: customLogger})
|
db, err = gorm.Open(sqlite.Open(sqliteDb), &gorm.Config{Logger: customLogger})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to connect to the DB: %v", err)
|
return nil, fmt.Errorf("failed to connect to the DB: %w", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
postgresDb := fmt.Sprintf(PostgresDb, os.Getenv("POSTGRESQL_PASSWORD"))
|
postgresDb := fmt.Sprintf(PostgresDb, os.Getenv("POSTGRESQL_PASSWORD"))
|
||||||
@ -535,7 +535,7 @@ func OpenDB() (*gorm.DB, error) {
|
|||||||
}
|
}
|
||||||
db, err = gormtrace.Open(postgres.New(postgres.Config{Conn: sqlDb}), &gorm.Config{Logger: customLogger})
|
db, err = gormtrace.Open(postgres.New(postgres.Config{Conn: sqlDb}), &gorm.Config{Logger: customLogger})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to connect to the DB: %v", err)
|
return nil, fmt.Errorf("failed to connect to the DB: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AddDatabaseTables(db)
|
AddDatabaseTables(db)
|
||||||
@ -602,12 +602,12 @@ type releaseInfo struct {
|
|||||||
func updateReleaseVersion() error {
|
func updateReleaseVersion() error {
|
||||||
resp, err := http.Get("https://api.github.com/repos/ddworken/hishtory/releases/latest")
|
resp, err := http.Get("https://api.github.com/repos/ddworken/hishtory/releases/latest")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get latest release version: %v", err)
|
return fmt.Errorf("failed to get latest release version: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
respBody, err := io.ReadAll(resp.Body)
|
respBody, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to read github API response body: %v", err)
|
return fmt.Errorf("failed to read github API response body: %w", err)
|
||||||
}
|
}
|
||||||
if resp.StatusCode == 403 && strings.Contains(string(respBody), "API rate limit exceeded for ") {
|
if resp.StatusCode == 403 && strings.Contains(string(respBody), "API rate limit exceeded for ") {
|
||||||
return nil
|
return nil
|
||||||
@ -618,7 +618,7 @@ func updateReleaseVersion() error {
|
|||||||
var info releaseInfo
|
var info releaseInfo
|
||||||
err = json.Unmarshal(respBody, &info)
|
err = json.Unmarshal(respBody, &info)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to parse github API response: %v", err)
|
return fmt.Errorf("failed to parse github API response: %w", err)
|
||||||
}
|
}
|
||||||
latestVersionTag := info.Name
|
latestVersionTag := info.Name
|
||||||
ReleaseVersion = decrementVersionIfInvalid(latestVersionTag)
|
ReleaseVersion = decrementVersionIfInvalid(latestVersionTag)
|
||||||
@ -654,7 +654,7 @@ func assertValidUpdate(updateInfo shared.UpdateInfo) error {
|
|||||||
for _, url := range urls {
|
for _, url := range urls {
|
||||||
resp, err := http.Get(url)
|
resp, err := http.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to retrieve URL %#v: %v", url, err)
|
return fmt.Errorf("failed to retrieve URL %#v: %w", url, err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
if resp.StatusCode == 404 {
|
if resp.StatusCode == 404 {
|
||||||
@ -878,7 +878,7 @@ func deepCleanDatabase(ctx context.Context) {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("failed to deep clean DB: %v", err))
|
panic(fmt.Errorf("failed to deep clean DB: %w", err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ func (b bashTester) RunInteractiveShellRelaxed(t testing.TB, script string) (str
|
|||||||
cmd.Stderr = &stderr
|
cmd.Stderr = &stderr
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("unexpected error when running commands, out=%#v, err=%#v: %v", stdout.String(), stderr.String(), err)
|
return "", fmt.Errorf("unexpected error when running commands, out=%#v, err=%#v: %w", stdout.String(), stderr.String(), err)
|
||||||
}
|
}
|
||||||
outStr := stdout.String()
|
outStr := stdout.String()
|
||||||
require.NotContains(t, outStr, "hishtory fatal error", "Ran command, but hishtory had a fatal error!")
|
require.NotContains(t, outStr, "hishtory fatal error", "Ran command, but hishtory had a fatal error!")
|
||||||
@ -105,7 +105,7 @@ func (z zshTester) RunInteractiveShellRelaxed(t testing.TB, script string) (stri
|
|||||||
cmd.Stderr = &stderr
|
cmd.Stderr = &stderr
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return stdout.String(), fmt.Errorf("unexpected error when running command=%#v, out=%#v, err=%#v: %v", script, stdout.String(), stderr.String(), err)
|
return stdout.String(), fmt.Errorf("unexpected error when running command=%#v, out=%#v, err=%#v: %w", script, stdout.String(), stderr.String(), err)
|
||||||
}
|
}
|
||||||
outStr := stdout.String()
|
outStr := stdout.String()
|
||||||
require.NotContains(t, outStr, "hishtory fatal error")
|
require.NotContains(t, outStr, "hishtory fatal error")
|
||||||
|
@ -129,7 +129,7 @@ func warnIfUnsupportedBashVersion() error {
|
|||||||
cmd := exec.Command("bash", "--version")
|
cmd := exec.Command("bash", "--version")
|
||||||
bashVersion, err := cmd.CombinedOutput()
|
bashVersion, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to check bash version: %v", err)
|
return fmt.Errorf("failed to check bash version: %w", err)
|
||||||
}
|
}
|
||||||
if strings.Contains(string(bashVersion), "version 3.") {
|
if strings.Contains(string(bashVersion), "version 3.") {
|
||||||
fmt.Printf("Warning: Your current bash version does not support overriding control-r. Please upgrade to at least bash 5 to enable the control-r integration.\n")
|
fmt.Printf("Warning: Your current bash version does not support overriding control-r. Please upgrade to at least bash 5 to enable the control-r integration.\n")
|
||||||
@ -140,7 +140,7 @@ func warnIfUnsupportedBashVersion() error {
|
|||||||
func install(secretKey string, offline bool) error {
|
func install(secretKey string, offline bool) error {
|
||||||
homedir, err := os.UserHomeDir()
|
homedir, err := os.UserHomeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get user's home directory: %v", err)
|
return fmt.Errorf("failed to get user's home directory: %w", err)
|
||||||
}
|
}
|
||||||
err = hctx.MakeHishtoryDir()
|
err = hctx.MakeHishtoryDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -201,16 +201,16 @@ func installBinary(homedir string) (string, error) {
|
|||||||
if _, err := os.Stat(clientPath); err == nil {
|
if _, err := os.Stat(clientPath); err == nil {
|
||||||
err = syscall.Unlink(clientPath)
|
err = syscall.Unlink(clientPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to unlink %s for install: %v", clientPath, err)
|
return "", fmt.Errorf("failed to unlink %s for install: %w", clientPath, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = copyFile(os.Args[0], clientPath)
|
err = copyFile(os.Args[0], clientPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to copy hishtory binary to $PATH: %v", err)
|
return "", fmt.Errorf("failed to copy hishtory binary to $PATH: %w", err)
|
||||||
}
|
}
|
||||||
err = os.Chmod(clientPath, 0o700)
|
err = os.Chmod(clientPath, 0o700)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to set permissions on hishtory binary: %v", err)
|
return "", fmt.Errorf("failed to set permissions on hishtory binary: %w", err)
|
||||||
}
|
}
|
||||||
return clientPath, nil
|
return clientPath, nil
|
||||||
}
|
}
|
||||||
@ -236,12 +236,12 @@ func configureFish(homedir, binaryPath string) error {
|
|||||||
}
|
}
|
||||||
err = os.WriteFile(getFishConfigPath(homedir), []byte(configContents), 0o644)
|
err = os.WriteFile(getFishConfigPath(homedir), []byte(configContents), 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to write config.zsh file: %v", err)
|
return fmt.Errorf("failed to write config.zsh file: %w", err)
|
||||||
}
|
}
|
||||||
// Check if we need to configure the fishrc
|
// Check if we need to configure the fishrc
|
||||||
fishIsConfigured, err := isFishConfigured(homedir)
|
fishIsConfigured, err := isFishConfigured(homedir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to check ~/.config/fish/config.fish: %v", err)
|
return fmt.Errorf("failed to check ~/.config/fish/config.fish: %w", err)
|
||||||
}
|
}
|
||||||
if fishIsConfigured {
|
if fishIsConfigured {
|
||||||
return nil
|
return nil
|
||||||
@ -249,7 +249,7 @@ func configureFish(homedir, binaryPath string) error {
|
|||||||
// Add to fishrc
|
// Add to fishrc
|
||||||
err = os.MkdirAll(path.Join(homedir, ".config/fish"), 0o744)
|
err = os.MkdirAll(path.Join(homedir, ".config/fish"), 0o744)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create fish config directory: %v", err)
|
return fmt.Errorf("failed to create fish config directory: %w", err)
|
||||||
}
|
}
|
||||||
return addToShellConfig(path.Join(homedir, ".config/fish/config.fish"), getFishConfigFragment(homedir))
|
return addToShellConfig(path.Join(homedir, ".config/fish/config.fish"), getFishConfigFragment(homedir))
|
||||||
}
|
}
|
||||||
@ -265,7 +265,7 @@ func isFishConfigured(homedir string) (bool, error) {
|
|||||||
}
|
}
|
||||||
fishConfig, err := os.ReadFile(path.Join(homedir, ".config/fish/config.fish"))
|
fishConfig, err := os.ReadFile(path.Join(homedir, ".config/fish/config.fish"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("failed to read ~/.config/fish/config.fish: %v", err)
|
return false, fmt.Errorf("failed to read ~/.config/fish/config.fish: %w", err)
|
||||||
}
|
}
|
||||||
return strings.Contains(string(fishConfig), getFishConfigFragment(homedir)), nil
|
return strings.Contains(string(fishConfig), getFishConfigFragment(homedir)), nil
|
||||||
}
|
}
|
||||||
@ -286,12 +286,12 @@ func configureZshrc(homedir, binaryPath string) error {
|
|||||||
}
|
}
|
||||||
err := os.WriteFile(getZshConfigPath(homedir), []byte(configContents), 0o644)
|
err := os.WriteFile(getZshConfigPath(homedir), []byte(configContents), 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to write config.zsh file: %v", err)
|
return fmt.Errorf("failed to write config.zsh file: %w", err)
|
||||||
}
|
}
|
||||||
// Check if we need to configure the zshrc
|
// Check if we need to configure the zshrc
|
||||||
zshIsConfigured, err := isZshConfigured(homedir)
|
zshIsConfigured, err := isZshConfigured(homedir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to check .zshrc: %v", err)
|
return fmt.Errorf("failed to check .zshrc: %w", err)
|
||||||
}
|
}
|
||||||
if zshIsConfigured {
|
if zshIsConfigured {
|
||||||
return nil
|
return nil
|
||||||
@ -318,7 +318,7 @@ func isZshConfigured(homedir string) (bool, error) {
|
|||||||
}
|
}
|
||||||
bashrc, err := os.ReadFile(getZshRcPath(homedir))
|
bashrc, err := os.ReadFile(getZshRcPath(homedir))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("failed to read zshrc: %v", err)
|
return false, fmt.Errorf("failed to read zshrc: %w", err)
|
||||||
}
|
}
|
||||||
return strings.Contains(string(bashrc), getZshConfigFragment(homedir)), nil
|
return strings.Contains(string(bashrc), getZshConfigFragment(homedir)), nil
|
||||||
}
|
}
|
||||||
@ -339,12 +339,12 @@ func configureBashrc(homedir, binaryPath string) error {
|
|||||||
}
|
}
|
||||||
err := os.WriteFile(getBashConfigPath(homedir), []byte(configContents), 0o644)
|
err := os.WriteFile(getBashConfigPath(homedir), []byte(configContents), 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to write config.sh file: %v", err)
|
return fmt.Errorf("failed to write config.sh file: %w", err)
|
||||||
}
|
}
|
||||||
// Check if we need to configure the bashrc and configure it if so
|
// Check if we need to configure the bashrc and configure it if so
|
||||||
bashRcIsConfigured, err := isBashRcConfigured(homedir)
|
bashRcIsConfigured, err := isBashRcConfigured(homedir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to check ~/.bashrc: %v", err)
|
return fmt.Errorf("failed to check ~/.bashrc: %w", err)
|
||||||
}
|
}
|
||||||
if !bashRcIsConfigured {
|
if !bashRcIsConfigured {
|
||||||
err = addToShellConfig(path.Join(homedir, ".bashrc"), getBashConfigFragment(homedir))
|
err = addToShellConfig(path.Join(homedir, ".bashrc"), getBashConfigFragment(homedir))
|
||||||
@ -356,7 +356,7 @@ func configureBashrc(homedir, binaryPath string) error {
|
|||||||
if doesBashProfileNeedConfig(homedir) {
|
if doesBashProfileNeedConfig(homedir) {
|
||||||
bashProfileIsConfigured, err := isBashProfileConfigured(homedir)
|
bashProfileIsConfigured, err := isBashProfileConfigured(homedir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to check ~/.bash_profile: %v", err)
|
return fmt.Errorf("failed to check ~/.bash_profile: %w", err)
|
||||||
}
|
}
|
||||||
if !bashProfileIsConfigured {
|
if !bashProfileIsConfigured {
|
||||||
err = addToShellConfig(path.Join(homedir, ".bash_profile"), getBashConfigFragment(homedir))
|
err = addToShellConfig(path.Join(homedir, ".bash_profile"), getBashConfigFragment(homedir))
|
||||||
@ -371,12 +371,12 @@ func configureBashrc(homedir, binaryPath string) error {
|
|||||||
func addToShellConfig(shellConfigPath, configFragment string) error {
|
func addToShellConfig(shellConfigPath, configFragment string) error {
|
||||||
f, err := os.OpenFile(shellConfigPath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o644)
|
f, err := os.OpenFile(shellConfigPath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to append to %s: %v", shellConfigPath, err)
|
return fmt.Errorf("failed to append to %s: %w", shellConfigPath, err)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
_, err = f.WriteString(configFragment)
|
_, err = f.WriteString(configFragment)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to append to %s: %v", shellConfigPath, err)
|
return fmt.Errorf("failed to append to %s: %w", shellConfigPath, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -392,7 +392,7 @@ func isBashRcConfigured(homedir string) (bool, error) {
|
|||||||
}
|
}
|
||||||
bashrc, err := os.ReadFile(path.Join(homedir, ".bashrc"))
|
bashrc, err := os.ReadFile(path.Join(homedir, ".bashrc"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("failed to read bashrc: %v", err)
|
return false, fmt.Errorf("failed to read bashrc: %w", err)
|
||||||
}
|
}
|
||||||
return strings.Contains(string(bashrc), getBashConfigFragment(homedir)), nil
|
return strings.Contains(string(bashrc), getBashConfigFragment(homedir)), nil
|
||||||
}
|
}
|
||||||
@ -418,7 +418,7 @@ func isBashProfileConfigured(homedir string) (bool, error) {
|
|||||||
}
|
}
|
||||||
bashrc, err := os.ReadFile(path.Join(homedir, ".bash_profile"))
|
bashrc, err := os.ReadFile(path.Join(homedir, ".bash_profile"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("failed to read bash_profile: %v", err)
|
return false, fmt.Errorf("failed to read bash_profile: %w", err)
|
||||||
}
|
}
|
||||||
return strings.Contains(string(bashrc), getBashConfigFragment(homedir)), nil
|
return strings.Contains(string(bashrc), getBashConfigFragment(homedir)), nil
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ func redact(ctx *context.Context, query string, force bool) error {
|
|||||||
reader := bufio.NewReader(os.Stdin)
|
reader := bufio.NewReader(os.Stdin)
|
||||||
resp, err := reader.ReadString('\n')
|
resp, err := reader.ReadString('\n')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to read response: %v", err)
|
return fmt.Errorf("failed to read response: %w", err)
|
||||||
}
|
}
|
||||||
if strings.TrimSpace(resp) != "y" {
|
if strings.TrimSpace(resp) != "y" {
|
||||||
fmt.Printf("Aborting delete per user response of %#v\n", strings.TrimSpace(resp))
|
fmt.Printf("Aborting delete per user response of %#v\n", strings.TrimSpace(resp))
|
||||||
|
@ -58,7 +58,7 @@ func maybeUploadSkippedHistoryEntries(ctx *context.Context) error {
|
|||||||
query := fmt.Sprintf("after:%s", time.Unix(config.MissedUploadTimestamp, 0).Format("2006-01-02"))
|
query := fmt.Sprintf("after:%s", time.Unix(config.MissedUploadTimestamp, 0).Format("2006-01-02"))
|
||||||
entries, err := lib.Search(ctx, db, query, 0)
|
entries, err := lib.Search(ctx, db, query, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to retrieve history entries that haven't been uploaded yet: %v", err)
|
return fmt.Errorf("failed to retrieve history entries that haven't been uploaded yet: %w", err)
|
||||||
}
|
}
|
||||||
hctx.GetLogger().Infof("Uploading %d history entries that previously failed to upload (query=%#v)\n", len(entries), query)
|
hctx.GetLogger().Infof("Uploading %d history entries that previously failed to upload (query=%#v)\n", len(entries), query)
|
||||||
jsonValue, err := lib.EncryptAndMarshal(config, entries)
|
jsonValue, err := lib.EncryptAndMarshal(config, entries)
|
||||||
@ -76,7 +76,7 @@ func maybeUploadSkippedHistoryEntries(ctx *context.Context) error {
|
|||||||
config.MissedUploadTimestamp = 0
|
config.MissedUploadTimestamp = 0
|
||||||
err = hctx.SetConfig(config)
|
err = hctx.SetConfig(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to mark a history entry as uploaded: %v", err)
|
return fmt.Errorf("failed to mark a history entry as uploaded: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -140,11 +140,11 @@ func saveHistoryEntry(ctx *context.Context) {
|
|||||||
if config.BetaMode {
|
if config.BetaMode {
|
||||||
tx, err := lib.MakeWhereQueryFromSearch(ctx, db, "cwd:"+entry.CurrentWorkingDirectory+" start_time:"+strconv.FormatInt(entry.StartTime.Unix(), 10))
|
tx, err := lib.MakeWhereQueryFromSearch(ctx, db, "cwd:"+entry.CurrentWorkingDirectory+" start_time:"+strconv.FormatInt(entry.StartTime.Unix(), 10))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lib.CheckFatalError(fmt.Errorf("failed to query for pre-saved history entries: %s", err))
|
lib.CheckFatalError(fmt.Errorf("failed to query for pre-saved history entries: %w", err))
|
||||||
}
|
}
|
||||||
res := tx.Delete(&data.HistoryEntry{})
|
res := tx.Delete(&data.HistoryEntry{})
|
||||||
if res.Error != nil {
|
if res.Error != nil {
|
||||||
lib.CheckFatalError(fmt.Errorf("failed to delete pre-saved history entries: %s", res.Error))
|
lib.CheckFatalError(fmt.Errorf("failed to delete pre-saved history entries: %w", res.Error))
|
||||||
}
|
}
|
||||||
if res.RowsAffected > 1 {
|
if res.RowsAffected > 1 {
|
||||||
lib.CheckFatalError(fmt.Errorf("attempted to delete pre-saved entry, but something went wrong since we deleted %d rows", res.RowsAffected))
|
lib.CheckFatalError(fmt.Errorf("attempted to delete pre-saved entry, but something went wrong since we deleted %d rows", res.RowsAffected))
|
||||||
@ -224,14 +224,14 @@ func buildPreArgsHistoryEntry(ctx *context.Context) (*data.HistoryEntry, error)
|
|||||||
// user
|
// user
|
||||||
user, err := user.Current()
|
user, err := user.Current()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to build history entry: %v", err)
|
return nil, fmt.Errorf("failed to build history entry: %w", err)
|
||||||
}
|
}
|
||||||
entry.LocalUsername = user.Username
|
entry.LocalUsername = user.Username
|
||||||
|
|
||||||
// cwd and homedir
|
// cwd and homedir
|
||||||
cwd, homedir, err := getCwd(ctx)
|
cwd, homedir, err := getCwd(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to build history entry: %v", err)
|
return nil, fmt.Errorf("failed to build history entry: %w", err)
|
||||||
}
|
}
|
||||||
entry.CurrentWorkingDirectory = cwd
|
entry.CurrentWorkingDirectory = cwd
|
||||||
entry.HomeDirectory = homedir
|
entry.HomeDirectory = homedir
|
||||||
@ -239,7 +239,7 @@ func buildPreArgsHistoryEntry(ctx *context.Context) (*data.HistoryEntry, error)
|
|||||||
// hostname
|
// hostname
|
||||||
hostname, err := os.Hostname()
|
hostname, err := os.Hostname()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to build history entry: %v", err)
|
return nil, fmt.Errorf("failed to build history entry: %w", err)
|
||||||
}
|
}
|
||||||
entry.Hostname = hostname
|
entry.Hostname = hostname
|
||||||
|
|
||||||
@ -272,14 +272,14 @@ func buildHistoryEntry(ctx *context.Context, args []string) (*data.HistoryEntry,
|
|||||||
// exitCode
|
// exitCode
|
||||||
exitCode, err := strconv.Atoi(args[3])
|
exitCode, err := strconv.Atoi(args[3])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to build history entry: %v", err)
|
return nil, fmt.Errorf("failed to build history entry: %w", err)
|
||||||
}
|
}
|
||||||
entry.ExitCode = exitCode
|
entry.ExitCode = exitCode
|
||||||
|
|
||||||
// start time
|
// start time
|
||||||
seconds, err := parseCrossPlatformInt(args[5])
|
seconds, err := parseCrossPlatformInt(args[5])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse start time %s as int: %v", args[5], err)
|
return nil, fmt.Errorf("failed to parse start time %s as int: %w", args[5], err)
|
||||||
}
|
}
|
||||||
entry.StartTime = time.Unix(seconds, 0)
|
entry.StartTime = time.Unix(seconds, 0)
|
||||||
|
|
||||||
@ -290,11 +290,11 @@ func buildHistoryEntry(ctx *context.Context, args []string) (*data.HistoryEntry,
|
|||||||
if shell == "bash" {
|
if shell == "bash" {
|
||||||
cmd, err := getLastCommand(args[4])
|
cmd, err := getLastCommand(args[4])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to build history entry: %v", err)
|
return nil, fmt.Errorf("failed to build history entry: %w", err)
|
||||||
}
|
}
|
||||||
shouldBeSkipped, err := shouldSkipHiddenCommand(ctx, args[4])
|
shouldBeSkipped, err := shouldSkipHiddenCommand(ctx, args[4])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to check if command was hidden: %v", err)
|
return nil, fmt.Errorf("failed to check if command was hidden: %w", err)
|
||||||
}
|
}
|
||||||
if shouldBeSkipped || strings.HasPrefix(cmd, " ") {
|
if shouldBeSkipped || strings.HasPrefix(cmd, " ") {
|
||||||
// Don't save commands that start with a space
|
// Don't save commands that start with a space
|
||||||
@ -443,7 +443,7 @@ func maybeSkipBashHistTimePrefix(cmdLine string) (string, error) {
|
|||||||
}
|
}
|
||||||
re, err := regexp.Compile("^" + buildRegexFromTimeFormat(format))
|
re, err := regexp.Compile("^" + buildRegexFromTimeFormat(format))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to parse regex for HISTTIMEFORMAT variable: %v", err)
|
return "", fmt.Errorf("failed to parse regex for HISTTIMEFORMAT variable: %w", err)
|
||||||
}
|
}
|
||||||
return re.ReplaceAllLiteralString(cmdLine, ""), nil
|
return re.ReplaceAllLiteralString(cmdLine, ""), nil
|
||||||
}
|
}
|
||||||
@ -481,7 +481,7 @@ func shouldSkipHiddenCommand(ctx *context.Context, historyLine string) (bool, er
|
|||||||
func getCwd(ctx *context.Context) (string, string, error) {
|
func getCwd(ctx *context.Context) (string, string, error) {
|
||||||
cwd, err := getCwdWithoutSubstitution()
|
cwd, err := getCwdWithoutSubstitution()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", fmt.Errorf("failed to get cwd for last command: %v", err)
|
return "", "", fmt.Errorf("failed to get cwd for last command: %w", err)
|
||||||
}
|
}
|
||||||
homedir := hctx.GetHome(ctx)
|
homedir := hctx.GetHome(ctx)
|
||||||
if cwd == homedir {
|
if cwd == homedir {
|
||||||
|
@ -96,16 +96,16 @@ func makeAead(userSecret string) (cipher.AEAD, error) {
|
|||||||
func Encrypt(userSecret string, data, additionalData []byte) ([]byte, []byte, error) {
|
func Encrypt(userSecret string, data, additionalData []byte) ([]byte, []byte, error) {
|
||||||
aead, err := makeAead(userSecret)
|
aead, err := makeAead(userSecret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []byte{}, []byte{}, fmt.Errorf("failed to make AEAD: %v", err)
|
return []byte{}, []byte{}, fmt.Errorf("failed to make AEAD: %w", err)
|
||||||
}
|
}
|
||||||
nonce := make([]byte, 12)
|
nonce := make([]byte, 12)
|
||||||
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||||||
return []byte{}, []byte{}, fmt.Errorf("failed to read a nonce: %v", err)
|
return []byte{}, []byte{}, fmt.Errorf("failed to read a nonce: %w", err)
|
||||||
}
|
}
|
||||||
ciphertext := aead.Seal(nil, nonce, data, additionalData)
|
ciphertext := aead.Seal(nil, nonce, data, additionalData)
|
||||||
_, err = aead.Open(nil, nonce, ciphertext, additionalData)
|
_, err = aead.Open(nil, nonce, ciphertext, additionalData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []byte{}, []byte{}, fmt.Errorf("failed to open AEAD: %v", err)
|
return []byte{}, []byte{}, fmt.Errorf("failed to open AEAD: %w", err)
|
||||||
}
|
}
|
||||||
return ciphertext, nonce, nil
|
return ciphertext, nonce, nil
|
||||||
}
|
}
|
||||||
@ -113,11 +113,11 @@ func Encrypt(userSecret string, data, additionalData []byte) ([]byte, []byte, er
|
|||||||
func Decrypt(userSecret string, data, additionalData, nonce []byte) ([]byte, error) {
|
func Decrypt(userSecret string, data, additionalData, nonce []byte) ([]byte, error) {
|
||||||
aead, err := makeAead(userSecret)
|
aead, err := makeAead(userSecret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []byte{}, fmt.Errorf("failed to make AEAD: %v", err)
|
return []byte{}, fmt.Errorf("failed to make AEAD: %w", err)
|
||||||
}
|
}
|
||||||
plaintext, err := aead.Open(nil, nonce, data, additionalData)
|
plaintext, err := aead.Open(nil, nonce, data, additionalData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []byte{}, fmt.Errorf("failed to decrypt: %v", err)
|
return []byte{}, fmt.Errorf("failed to decrypt: %w", err)
|
||||||
}
|
}
|
||||||
return plaintext, nil
|
return plaintext, nil
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ func GetLogger() *logrus.Logger {
|
|||||||
getLoggerOnce.Do(func() {
|
getLoggerOnce.Do(func() {
|
||||||
homedir, err := os.UserHomeDir()
|
homedir, err := os.UserHomeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("failed to get user's home directory: %v", err))
|
panic(fmt.Errorf("failed to get user's home directory: %w", err))
|
||||||
}
|
}
|
||||||
err = MakeHishtoryDir()
|
err = MakeHishtoryDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -59,11 +59,11 @@ func GetLogger() *logrus.Logger {
|
|||||||
func MakeHishtoryDir() error {
|
func MakeHishtoryDir() error {
|
||||||
homedir, err := os.UserHomeDir()
|
homedir, err := os.UserHomeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get user's home directory: %v", err)
|
return fmt.Errorf("failed to get user's home directory: %w", err)
|
||||||
}
|
}
|
||||||
err = os.MkdirAll(path.Join(homedir, data.GetHishtoryPath()), 0o744)
|
err = os.MkdirAll(path.Join(homedir, data.GetHishtoryPath()), 0o744)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create ~/%s dir: %v", data.GetHishtoryPath(), err)
|
return fmt.Errorf("failed to create ~/%s dir: %w", data.GetHishtoryPath(), err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -71,7 +71,7 @@ func MakeHishtoryDir() error {
|
|||||||
func OpenLocalSqliteDb() (*gorm.DB, error) {
|
func OpenLocalSqliteDb() (*gorm.DB, error) {
|
||||||
homedir, err := os.UserHomeDir()
|
homedir, err := os.UserHomeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get user's home directory: %v", err)
|
return nil, fmt.Errorf("failed to get user's home directory: %w", err)
|
||||||
}
|
}
|
||||||
err = MakeHishtoryDir()
|
err = MakeHishtoryDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -90,7 +90,7 @@ func OpenLocalSqliteDb() (*gorm.DB, error) {
|
|||||||
dsn := fmt.Sprintf("file:%s?mode=rwc&_journal_mode=WAL", dbFilePath)
|
dsn := fmt.Sprintf("file:%s?mode=rwc&_journal_mode=WAL", dbFilePath)
|
||||||
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{SkipDefaultTransaction: true, Logger: newLogger})
|
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{SkipDefaultTransaction: true, Logger: newLogger})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to connect to the DB: %v", err)
|
return nil, fmt.Errorf("failed to connect to the DB: %w", err)
|
||||||
}
|
}
|
||||||
tx, err := db.DB()
|
tx, err := db.DB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -112,17 +112,17 @@ func MakeContext() *context.Context {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
config, err := GetConfig()
|
config, err := GetConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("failed to retrieve config: %v", err))
|
panic(fmt.Errorf("failed to retrieve config: %w", err))
|
||||||
}
|
}
|
||||||
ctx = context.WithValue(ctx, hishtoryContextKey("config"), config)
|
ctx = context.WithValue(ctx, hishtoryContextKey("config"), config)
|
||||||
db, err := OpenLocalSqliteDb()
|
db, err := OpenLocalSqliteDb()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("failed to open local DB: %v", err))
|
panic(fmt.Errorf("failed to open local DB: %w", err))
|
||||||
}
|
}
|
||||||
ctx = context.WithValue(ctx, hishtoryContextKey("db"), db)
|
ctx = context.WithValue(ctx, hishtoryContextKey("db"), db)
|
||||||
homedir, err := os.UserHomeDir()
|
homedir, err := os.UserHomeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("failed to get homedir: %v", err))
|
panic(fmt.Errorf("failed to get homedir: %w", err))
|
||||||
}
|
}
|
||||||
ctx = context.WithValue(ctx, hishtoryContextKey("homedir"), homedir)
|
ctx = context.WithValue(ctx, hishtoryContextKey("homedir"), homedir)
|
||||||
return &ctx
|
return &ctx
|
||||||
@ -191,20 +191,20 @@ type CustomColumnDefinition struct {
|
|||||||
func GetConfigContents() ([]byte, error) {
|
func GetConfigContents() ([]byte, error) {
|
||||||
homedir, err := os.UserHomeDir()
|
homedir, err := os.UserHomeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to retrieve homedir: %v", err)
|
return nil, fmt.Errorf("failed to retrieve homedir: %w", err)
|
||||||
}
|
}
|
||||||
dat, err := os.ReadFile(path.Join(homedir, data.GetHishtoryPath(), data.CONFIG_PATH))
|
dat, err := os.ReadFile(path.Join(homedir, data.GetHishtoryPath(), data.CONFIG_PATH))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
files, err := os.ReadDir(path.Join(homedir, data.GetHishtoryPath()))
|
files, err := os.ReadDir(path.Join(homedir, data.GetHishtoryPath()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to read config file (and failed to list too): %v", err)
|
return nil, fmt.Errorf("failed to read config file (and failed to list too): %w", err)
|
||||||
}
|
}
|
||||||
filenames := ""
|
filenames := ""
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
filenames += file.Name()
|
filenames += file.Name()
|
||||||
filenames += ", "
|
filenames += ", "
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("failed to read config file (files in HISHTORY_PATH: %s): %v", filenames, err)
|
return nil, fmt.Errorf("failed to read config file (files in HISHTORY_PATH: %s): %w", filenames, err)
|
||||||
}
|
}
|
||||||
return dat, nil
|
return dat, nil
|
||||||
}
|
}
|
||||||
@ -217,7 +217,7 @@ func GetConfig() (ClientConfig, error) {
|
|||||||
var config ClientConfig
|
var config ClientConfig
|
||||||
err = json.Unmarshal(data, &config)
|
err = json.Unmarshal(data, &config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ClientConfig{}, fmt.Errorf("failed to parse config file: %v", err)
|
return ClientConfig{}, fmt.Errorf("failed to parse config file: %w", err)
|
||||||
}
|
}
|
||||||
if config.DisplayedColumns == nil || len(config.DisplayedColumns) == 0 {
|
if config.DisplayedColumns == nil || len(config.DisplayedColumns) == 0 {
|
||||||
config.DisplayedColumns = []string{"Hostname", "CWD", "Timestamp", "Runtime", "Exit Code", "Command"}
|
config.DisplayedColumns = []string{"Hostname", "CWD", "Timestamp", "Runtime", "Exit Code", "Command"}
|
||||||
@ -231,11 +231,11 @@ func GetConfig() (ClientConfig, error) {
|
|||||||
func SetConfig(config ClientConfig) error {
|
func SetConfig(config ClientConfig) error {
|
||||||
serializedConfig, err := json.Marshal(config)
|
serializedConfig, err := json.Marshal(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to serialize config: %v", err)
|
return fmt.Errorf("failed to serialize config: %w", err)
|
||||||
}
|
}
|
||||||
homedir, err := os.UserHomeDir()
|
homedir, err := os.UserHomeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to retrieve homedir: %v", err)
|
return fmt.Errorf("failed to retrieve homedir: %w", err)
|
||||||
}
|
}
|
||||||
err = MakeHishtoryDir()
|
err = MakeHishtoryDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -245,11 +245,11 @@ func SetConfig(config ClientConfig) error {
|
|||||||
stagedConfigPath := configPath + ".tmp-" + uuid.Must(uuid.NewRandom()).String()
|
stagedConfigPath := configPath + ".tmp-" + uuid.Must(uuid.NewRandom()).String()
|
||||||
err = os.WriteFile(stagedConfigPath, serializedConfig, 0o644)
|
err = os.WriteFile(stagedConfigPath, serializedConfig, 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to write config: %v", err)
|
return fmt.Errorf("failed to write config: %w", err)
|
||||||
}
|
}
|
||||||
err = os.Rename(stagedConfigPath, configPath)
|
err = os.Rename(stagedConfigPath, configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to replace config file with the updated version: %v", err)
|
return fmt.Errorf("failed to replace config file with the updated version: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ import (
|
|||||||
"github.com/araddon/dateparse"
|
"github.com/araddon/dateparse"
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"github.com/mattn/go-sqlite3"
|
||||||
"github.com/rodaine/table"
|
"github.com/rodaine/table"
|
||||||
|
|
||||||
"github.com/ddworken/hishtory/client/data"
|
"github.com/ddworken/hishtory/client/data"
|
||||||
@ -66,7 +67,7 @@ func Setup(userSecret string, isOffline bool) error {
|
|||||||
config.IsOffline = isOffline
|
config.IsOffline = isOffline
|
||||||
err := hctx.SetConfig(config)
|
err := hctx.SetConfig(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to persist config to disk: %v", err)
|
return fmt.Errorf("failed to persist config to disk: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drop all existing data
|
// Drop all existing data
|
||||||
@ -82,22 +83,22 @@ func Setup(userSecret string, isOffline bool) error {
|
|||||||
}
|
}
|
||||||
_, err = ApiGet("/api/v1/register?user_id=" + data.UserId(userSecret) + "&device_id=" + config.DeviceId)
|
_, err = ApiGet("/api/v1/register?user_id=" + data.UserId(userSecret) + "&device_id=" + config.DeviceId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to register device with backend: %v", err)
|
return fmt.Errorf("failed to register device with backend: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
respBody, err := ApiGet("/api/v1/bootstrap?user_id=" + data.UserId(userSecret) + "&device_id=" + config.DeviceId)
|
respBody, err := ApiGet("/api/v1/bootstrap?user_id=" + data.UserId(userSecret) + "&device_id=" + config.DeviceId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to bootstrap device from the backend: %v", err)
|
return fmt.Errorf("failed to bootstrap device from the backend: %w", err)
|
||||||
}
|
}
|
||||||
var retrievedEntries []*shared.EncHistoryEntry
|
var retrievedEntries []*shared.EncHistoryEntry
|
||||||
err = json.Unmarshal(respBody, &retrievedEntries)
|
err = json.Unmarshal(respBody, &retrievedEntries)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to load JSON response: %v", err)
|
return fmt.Errorf("failed to load JSON response: %w", err)
|
||||||
}
|
}
|
||||||
for _, entry := range retrievedEntries {
|
for _, entry := range retrievedEntries {
|
||||||
decEntry, err := data.DecryptHistoryEntry(userSecret, *entry)
|
decEntry, err := data.DecryptHistoryEntry(userSecret, *entry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to decrypt history entry from server: %v", err)
|
return fmt.Errorf("failed to decrypt history entry from server: %w", err)
|
||||||
}
|
}
|
||||||
AddToDbIfNew(db, decEntry)
|
AddToDbIfNew(db, decEntry)
|
||||||
}
|
}
|
||||||
@ -252,30 +253,30 @@ func ImportHistory(ctx *context.Context, shouldReadStdin, force bool) (int, erro
|
|||||||
bashHistPath := filepath.Join(homedir, ".bash_history")
|
bashHistPath := filepath.Join(homedir, ".bash_history")
|
||||||
historyEntries, err := readFileToArray(bashHistPath)
|
historyEntries, err := readFileToArray(bashHistPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("failed to parse bash history: %v", err)
|
return 0, fmt.Errorf("failed to parse bash history: %w", err)
|
||||||
}
|
}
|
||||||
zshHistPath := filepath.Join(homedir, ".zsh_history")
|
zshHistPath := filepath.Join(homedir, ".zsh_history")
|
||||||
extraEntries, err := readFileToArray(zshHistPath)
|
extraEntries, err := readFileToArray(zshHistPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("failed to parse zsh history: %v", err)
|
return 0, fmt.Errorf("failed to parse zsh history: %w", err)
|
||||||
}
|
}
|
||||||
historyEntries = append(historyEntries, extraEntries...)
|
historyEntries = append(historyEntries, extraEntries...)
|
||||||
extraEntries, err = parseFishHistory(homedir)
|
extraEntries, err = parseFishHistory(homedir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("failed to parse fish history: %v", err)
|
return 0, fmt.Errorf("failed to parse fish history: %w", err)
|
||||||
}
|
}
|
||||||
historyEntries = append(historyEntries, extraEntries...)
|
historyEntries = append(historyEntries, extraEntries...)
|
||||||
if histfile := os.Getenv("HISTFILE"); histfile != "" && histfile != zshHistPath && histfile != bashHistPath {
|
if histfile := os.Getenv("HISTFILE"); histfile != "" && histfile != zshHistPath && histfile != bashHistPath {
|
||||||
extraEntries, err := readFileToArray(histfile)
|
extraEntries, err := readFileToArray(histfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("failed to parse histfile: %v", err)
|
return 0, fmt.Errorf("failed to parse histfile: %w", err)
|
||||||
}
|
}
|
||||||
historyEntries = append(historyEntries, extraEntries...)
|
historyEntries = append(historyEntries, extraEntries...)
|
||||||
}
|
}
|
||||||
if shouldReadStdin {
|
if shouldReadStdin {
|
||||||
extraEntries, err = readStdin()
|
extraEntries, err = readStdin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("failed to read stdin: %v", err)
|
return 0, fmt.Errorf("failed to read stdin: %w", err)
|
||||||
}
|
}
|
||||||
historyEntries = append(historyEntries, extraEntries...)
|
historyEntries = append(historyEntries, extraEntries...)
|
||||||
}
|
}
|
||||||
@ -307,17 +308,17 @@ func ImportHistory(ctx *context.Context, shouldReadStdin, force bool) (int, erro
|
|||||||
}
|
}
|
||||||
err = ReliableDbCreate(db, entry)
|
err = ReliableDbCreate(db, entry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("failed to insert imported history entry: %v", err)
|
return 0, fmt.Errorf("failed to insert imported history entry: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = Reupload(ctx)
|
err = Reupload(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("failed to upload hishtory import: %v", err)
|
return 0, fmt.Errorf("failed to upload hishtory import: %w", err)
|
||||||
}
|
}
|
||||||
config.HaveCompletedInitialImport = true
|
config.HaveCompletedInitialImport = true
|
||||||
err = hctx.SetConfig(config)
|
err = hctx.SetConfig(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("failed to mark initial import as completed, this may lead to duplicate history entries: %v", err)
|
return 0, fmt.Errorf("failed to mark initial import as completed, this may lead to duplicate history entries: %w", err)
|
||||||
}
|
}
|
||||||
// Trigger a checkpoint so that these bulk entries are added from the WAL to the main DB
|
// Trigger a checkpoint so that these bulk entries are added from the WAL to the main DB
|
||||||
db.Exec("PRAGMA wal_checkpoint")
|
db.Exec("PRAGMA wal_checkpoint")
|
||||||
@ -386,12 +387,12 @@ func readFileToArray(path string) ([]string, error) {
|
|||||||
func GetDownloadData() (shared.UpdateInfo, error) {
|
func GetDownloadData() (shared.UpdateInfo, error) {
|
||||||
respBody, err := ApiGet("/api/v1/download")
|
respBody, err := ApiGet("/api/v1/download")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return shared.UpdateInfo{}, fmt.Errorf("failed to download update info: %v", err)
|
return shared.UpdateInfo{}, fmt.Errorf("failed to download update info: %w", err)
|
||||||
}
|
}
|
||||||
var downloadData shared.UpdateInfo
|
var downloadData shared.UpdateInfo
|
||||||
err = json.Unmarshal(respBody, &downloadData)
|
err = json.Unmarshal(respBody, &downloadData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return shared.UpdateInfo{}, fmt.Errorf("failed to parse update info: %v", err)
|
return shared.UpdateInfo{}, fmt.Errorf("failed to parse update info: %w", err)
|
||||||
}
|
}
|
||||||
return downloadData, nil
|
return downloadData, nil
|
||||||
}
|
}
|
||||||
@ -438,7 +439,7 @@ func Update(ctx *context.Context) error {
|
|||||||
homedir := hctx.GetHome(ctx)
|
homedir := hctx.GetHome(ctx)
|
||||||
err = syscall.Unlink(path.Join(homedir, data.GetHishtoryPath(), "hishtory"))
|
err = syscall.Unlink(path.Join(homedir, data.GetHishtoryPath(), "hishtory"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to unlink %s for update: %v", path.Join(homedir, data.GetHishtoryPath(), "hishtory"), err)
|
return fmt.Errorf("failed to unlink %s for update: %w", path.Join(homedir, data.GetHishtoryPath(), "hishtory"), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -450,7 +451,7 @@ func Update(ctx *context.Context) error {
|
|||||||
cmd.Stderr = &stderr
|
cmd.Stderr = &stderr
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to chmod +x the update (stdout=%#v, stderr=%#v): %v", stdout.String(), stderr.String(), err)
|
return fmt.Errorf("failed to chmod +x the update (stdout=%#v, stderr=%#v): %w", stdout.String(), stderr.String(), err)
|
||||||
}
|
}
|
||||||
cmd = exec.Command(getTmpClientPath(), "install")
|
cmd = exec.Command(getTmpClientPath(), "install")
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
@ -458,7 +459,7 @@ func Update(ctx *context.Context) error {
|
|||||||
cmd.Stdin = os.Stdin
|
cmd.Stdin = os.Stdin
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to install update (stderr=%#v), is %s in a noexec directory? (if so, set the TMPDIR environment variable): %v", stderr.String(), getTmpClientPath(), err)
|
return fmt.Errorf("failed to install update (stderr=%#v), is %s in a noexec directory? (if so, set the TMPDIR environment variable): %w", stderr.String(), getTmpClientPath(), err)
|
||||||
}
|
}
|
||||||
fmt.Printf("Successfully updated hishtory from v0.%s to %s\n", Version, downloadData.Version)
|
fmt.Printf("Successfully updated hishtory from v0.%s to %s\n", Version, downloadData.Version)
|
||||||
return nil
|
return nil
|
||||||
@ -550,7 +551,7 @@ func stripCodeSignature(inPath, outPath string) error {
|
|||||||
cmd.Stderr = &stderr
|
cmd.Stderr = &stderr
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to use codesign_allocate to strip signatures on binary=%v (stdout=%#v, stderr%#v): %v", inPath, stdout.String(), stderr.String(), err)
|
return fmt.Errorf("failed to use codesign_allocate to strip signatures on binary=%v (stdout=%#v, stderr%#v): %w", inPath, stdout.String(), stderr.String(), err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -591,7 +592,7 @@ func downloadFile(filename, url string) error {
|
|||||||
// Download the data
|
// Download the data
|
||||||
resp, err := http.Get(url)
|
resp, err := http.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to download file at %s to %s: %v", url, filename, err)
|
return fmt.Errorf("failed to download file at %s to %s: %w", url, filename, err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
if resp.StatusCode != 200 {
|
if resp.StatusCode != 200 {
|
||||||
@ -609,7 +610,7 @@ func downloadFile(filename, url string) error {
|
|||||||
// Create the file
|
// Create the file
|
||||||
out, err := os.Create(filename)
|
out, err := os.Create(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to save file to %s: %v", filename, err)
|
return fmt.Errorf("failed to save file to %s: %w", filename, err)
|
||||||
}
|
}
|
||||||
defer out.Close()
|
defer out.Close()
|
||||||
|
|
||||||
@ -636,12 +637,12 @@ func ApiGet(path string) ([]byte, error) {
|
|||||||
start := time.Now()
|
start := time.Now()
|
||||||
req, err := http.NewRequest("GET", getServerHostname()+path, nil)
|
req, err := http.NewRequest("GET", getServerHostname()+path, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create GET: %v", err)
|
return nil, fmt.Errorf("failed to create GET: %w", err)
|
||||||
}
|
}
|
||||||
req.Header.Set("X-Hishtory-Version", "v0."+Version)
|
req.Header.Set("X-Hishtory-Version", "v0."+Version)
|
||||||
resp, err := httpClient().Do(req)
|
resp, err := httpClient().Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to GET %s%s: %v", getServerHostname(), path, err)
|
return nil, fmt.Errorf("failed to GET %s%s: %w", getServerHostname(), path, err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
if resp.StatusCode != 200 {
|
if resp.StatusCode != 200 {
|
||||||
@ -649,7 +650,7 @@ func ApiGet(path string) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
respBody, err := io.ReadAll(resp.Body)
|
respBody, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to read response body from GET %s%s: %v", getServerHostname(), path, err)
|
return nil, fmt.Errorf("failed to read response body from GET %s%s: %w", getServerHostname(), path, err)
|
||||||
}
|
}
|
||||||
duration := time.Since(start)
|
duration := time.Since(start)
|
||||||
hctx.GetLogger().Infof("ApiGet(%#v): %s\n", path, duration.String())
|
hctx.GetLogger().Infof("ApiGet(%#v): %s\n", path, duration.String())
|
||||||
@ -663,13 +664,13 @@ func ApiPost(path, contentType string, data []byte) ([]byte, error) {
|
|||||||
start := time.Now()
|
start := time.Now()
|
||||||
req, err := http.NewRequest("POST", getServerHostname()+path, bytes.NewBuffer(data))
|
req, err := http.NewRequest("POST", getServerHostname()+path, bytes.NewBuffer(data))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create POST: %v", err)
|
return nil, fmt.Errorf("failed to create POST: %w", err)
|
||||||
}
|
}
|
||||||
req.Header.Set("Content-Type", contentType)
|
req.Header.Set("Content-Type", contentType)
|
||||||
req.Header.Set("X-Hishtory-Version", "v0."+Version)
|
req.Header.Set("X-Hishtory-Version", "v0."+Version)
|
||||||
resp, err := httpClient().Do(req)
|
resp, err := httpClient().Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to POST %s: %v", path, err)
|
return nil, fmt.Errorf("failed to POST %s: %w", path, err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
if resp.StatusCode != 200 {
|
if resp.StatusCode != 200 {
|
||||||
@ -677,7 +678,7 @@ func ApiPost(path, contentType string, data []byte) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
respBody, err := io.ReadAll(resp.Body)
|
respBody, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to read response body from POST %s: %v", path, err)
|
return nil, fmt.Errorf("failed to read response body from POST %s: %w", path, err)
|
||||||
}
|
}
|
||||||
duration := time.Since(start)
|
duration := time.Since(start)
|
||||||
hctx.GetLogger().Infof("ApiPost(%#v): %s\n", path, duration.String())
|
hctx.GetLogger().Infof("ApiPost(%#v): %s\n", path, duration.String())
|
||||||
@ -708,26 +709,25 @@ func ReliableDbCreate(db *gorm.DB, entry interface{}) error {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
|
||||||
errMsg := err.Error()
|
//errMsg := err.Error()
|
||||||
if errMsg == "database is locked (5) (SQLITE_BUSY)" || errMsg == "database is locked (261)" {
|
if errors.Is(err, sqlite3.ErrBusy) || errors.Is(err, sqlite3.ErrLocked) {
|
||||||
time.Sleep(time.Duration(i*rand.Intn(100)) * time.Millisecond)
|
// accounts for wrapped errors like:
|
||||||
continue
|
// * "database is locked (5) (SQLITE_BUSY)"
|
||||||
|
// * "database is locked (261)" -- 261 is SQLITE_BUSY_RECOVERY (5 || 1<<8)
|
||||||
|
time.Sleep(time.Duration(i*rand.Intn(100)) * time.Millisecond)
|
||||||
|
continue
|
||||||
|
} else if errors.Is(err, sqlite3.ErrConstraintUnique) {
|
||||||
|
//if strings.Contains(errMsg, "UNIQUE constraint failed") {
|
||||||
|
if i == 0 {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
if strings.Contains(errMsg, "UNIQUE constraint failed") {
|
|
||||||
if i == 0 {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fmt.Errorf("unrecoverable sqlite error: %v", err)
|
|
||||||
}
|
|
||||||
if err != nil && err.Error() != "database is locked (5) (SQLITE_BUSY)" {
|
|
||||||
return fmt.Errorf("unrecoverable sqlite error: %v", err)
|
|
||||||
}
|
}
|
||||||
|
return fmt.Errorf("unrecoverable sqlite error: %w", err)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("failed to create DB entry even with %d retries: %v", i, err)
|
return fmt.Errorf("failed to create DB entry even with %d retries: %w", i, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func EncryptAndMarshal(config hctx.ClientConfig, entries []*data.HistoryEntry) ([]byte, error) {
|
func EncryptAndMarshal(config hctx.ClientConfig, entries []*data.HistoryEntry) ([]byte, error) {
|
||||||
@ -742,7 +742,7 @@ func EncryptAndMarshal(config hctx.ClientConfig, entries []*data.HistoryEntry) (
|
|||||||
}
|
}
|
||||||
jsonValue, err := json.Marshal(encEntries)
|
jsonValue, err := json.Marshal(encEntries)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return jsonValue, fmt.Errorf("failed to marshal encrypted history entry: %v", err)
|
return jsonValue, fmt.Errorf("failed to marshal encrypted history entry: %w", err)
|
||||||
}
|
}
|
||||||
return jsonValue, nil
|
return jsonValue, nil
|
||||||
}
|
}
|
||||||
@ -754,16 +754,16 @@ func Reupload(ctx *context.Context) error {
|
|||||||
}
|
}
|
||||||
entries, err := Search(ctx, hctx.GetDb(ctx), "", 0)
|
entries, err := Search(ctx, hctx.GetDb(ctx), "", 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to reupload due to failed search: %v", err)
|
return fmt.Errorf("failed to reupload due to failed search: %w", err)
|
||||||
}
|
}
|
||||||
for _, chunk := range shared.Chunks(entries, 100) {
|
for _, chunk := range shared.Chunks(entries, 100) {
|
||||||
jsonValue, err := EncryptAndMarshal(config, chunk)
|
jsonValue, err := EncryptAndMarshal(config, chunk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to reupload due to failed encryption: %v", err)
|
return fmt.Errorf("failed to reupload due to failed encryption: %w", err)
|
||||||
}
|
}
|
||||||
_, err = ApiPost("/api/v1/submit?source_device_id="+config.DeviceId, "application/json", jsonValue)
|
_, err = ApiPost("/api/v1/submit?source_device_id="+config.DeviceId, "application/json", jsonValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to reupload due to failed POST: %v", err)
|
return fmt.Errorf("failed to reupload due to failed POST: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -785,12 +785,12 @@ func RetrieveAdditionalEntriesFromRemote(ctx *context.Context) error {
|
|||||||
var retrievedEntries []*shared.EncHistoryEntry
|
var retrievedEntries []*shared.EncHistoryEntry
|
||||||
err = json.Unmarshal(respBody, &retrievedEntries)
|
err = json.Unmarshal(respBody, &retrievedEntries)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to load JSON response: %v", err)
|
return fmt.Errorf("failed to load JSON response: %w", err)
|
||||||
}
|
}
|
||||||
for _, entry := range retrievedEntries {
|
for _, entry := range retrievedEntries {
|
||||||
decEntry, err := data.DecryptHistoryEntry(config.UserSecret, *entry)
|
decEntry, err := data.DecryptHistoryEntry(config.UserSecret, *entry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to decrypt history entry from server: %v", err)
|
return fmt.Errorf("failed to decrypt history entry from server: %w", err)
|
||||||
}
|
}
|
||||||
AddToDbIfNew(db, decEntry)
|
AddToDbIfNew(db, decEntry)
|
||||||
}
|
}
|
||||||
@ -819,7 +819,7 @@ func ProcessDeletionRequests(ctx *context.Context) error {
|
|||||||
for _, entry := range request.Messages.Ids {
|
for _, entry := range request.Messages.Ids {
|
||||||
res := db.Where("device_id = ? AND end_time = ?", entry.DeviceId, entry.Date).Delete(&data.HistoryEntry{})
|
res := db.Where("device_id = ? AND end_time = ?", entry.DeviceId, entry.Date).Delete(&data.HistoryEntry{})
|
||||||
if res.Error != nil {
|
if res.Error != nil {
|
||||||
return fmt.Errorf("DB error: %v", res.Error)
|
return fmt.Errorf("DB error: %w", res.Error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -843,7 +843,7 @@ func parseTimeGenerously(input string) (time.Time, error) {
|
|||||||
func MakeWhereQueryFromSearch(ctx *context.Context, db *gorm.DB, query string) (*gorm.DB, error) {
|
func MakeWhereQueryFromSearch(ctx *context.Context, db *gorm.DB, query string) (*gorm.DB, error) {
|
||||||
tokens, err := tokenize(query)
|
tokens, err := tokenize(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to tokenize query: %v", err)
|
return nil, fmt.Errorf("failed to tokenize query: %w", err)
|
||||||
}
|
}
|
||||||
tx := db.Model(&data.HistoryEntry{}).Where("true")
|
tx := db.Model(&data.HistoryEntry{}).Where("true")
|
||||||
for _, token := range tokens {
|
for _, token := range tokens {
|
||||||
@ -903,7 +903,7 @@ func Search(ctx *context.Context, db *gorm.DB, query string, limit int) ([]*data
|
|||||||
var historyEntries []*data.HistoryEntry
|
var historyEntries []*data.HistoryEntry
|
||||||
result := tx.Find(&historyEntries)
|
result := tx.Find(&historyEntries)
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
return nil, fmt.Errorf("DB query error: %v", result.Error)
|
return nil, fmt.Errorf("DB query error: %w", result.Error)
|
||||||
}
|
}
|
||||||
return historyEntries, nil
|
return historyEntries, nil
|
||||||
}
|
}
|
||||||
@ -931,13 +931,13 @@ func parseAtomizedToken(ctx *context.Context, token string) (string, interface{}
|
|||||||
case "before":
|
case "before":
|
||||||
t, err := parseTimeGenerously(val)
|
t, err := parseTimeGenerously(val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, nil, fmt.Errorf("failed to parse before:%s as a timestamp: %v", val, err)
|
return "", nil, nil, fmt.Errorf("failed to parse before:%s as a timestamp: %w", val, err)
|
||||||
}
|
}
|
||||||
return "(CAST(strftime(\"%s\",start_time) AS INTEGER) < ?)", t.Unix(), nil, nil
|
return "(CAST(strftime(\"%s\",start_time) AS INTEGER) < ?)", t.Unix(), nil, nil
|
||||||
case "after":
|
case "after":
|
||||||
t, err := parseTimeGenerously(val)
|
t, err := parseTimeGenerously(val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, nil, fmt.Errorf("failed to parse after:%s as a timestamp: %v", val, err)
|
return "", nil, nil, fmt.Errorf("failed to parse after:%s as a timestamp: %w", val, err)
|
||||||
}
|
}
|
||||||
return "(CAST(strftime(\"%s\",start_time) AS INTEGER) > ?)", t.Unix(), nil, nil
|
return "(CAST(strftime(\"%s\",start_time) AS INTEGER) > ?)", t.Unix(), nil, nil
|
||||||
case "start_time":
|
case "start_time":
|
||||||
@ -945,7 +945,7 @@ func parseAtomizedToken(ctx *context.Context, token string) (string, interface{}
|
|||||||
// internally for pre-saving history entries.
|
// internally for pre-saving history entries.
|
||||||
t, err := parseTimeGenerously(val)
|
t, err := parseTimeGenerously(val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, nil, fmt.Errorf("failed to parse after:%s as a timestamp: %v", val, err)
|
return "", nil, nil, fmt.Errorf("failed to parse after:%s as a timestamp: %w", val, err)
|
||||||
}
|
}
|
||||||
return "(CAST(strftime(\"%s\",start_time) AS INTEGER) = ?)", t.Unix(), nil, nil
|
return "(CAST(strftime(\"%s\",start_time) AS INTEGER) = ?)", t.Unix(), nil, nil
|
||||||
case "command":
|
case "command":
|
||||||
@ -960,7 +960,7 @@ func parseAtomizedToken(ctx *context.Context, token string) (string, interface{}
|
|||||||
// Also get all ones that are in the DB
|
// Also get all ones that are in the DB
|
||||||
names, err := getAllCustomColumnNames(ctx)
|
names, err := getAllCustomColumnNames(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, nil, fmt.Errorf("failed to get custom column names from the DB: %v", err)
|
return "", nil, nil, fmt.Errorf("failed to get custom column names from the DB: %w", err)
|
||||||
}
|
}
|
||||||
knownCustomColumns = append(knownCustomColumns, names...)
|
knownCustomColumns = append(knownCustomColumns, names...)
|
||||||
// Check if the atom is for a custom column that exists and if it isn't, return an error
|
// Check if the atom is for a custom column that exists and if it isn't, return an error
|
||||||
@ -1079,7 +1079,7 @@ func SendDeletionRequest(deletionRequest shared.DeletionRequest) error {
|
|||||||
}
|
}
|
||||||
_, err = ApiPost("/api/v1/add-deletion-request", "application/json", data)
|
_, err = ApiPost("/api/v1/add-deletion-request", "application/json", data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to send deletion request to backend service, this may cause commands to not get deleted on other instances of hishtory: %v", err)
|
return fmt.Errorf("failed to send deletion request to backend service, this may cause commands to not get deleted on other instances of hishtory: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ func verifyBinary(ctx *context.Context, binaryPath, attestationPath, versionTag
|
|||||||
|
|
||||||
attestation, err := os.ReadFile(attestationPath)
|
attestation, err := os.ReadFile(attestationPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to read attestation file: %v", err)
|
return fmt.Errorf("failed to read attestation file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
hash, err := getFileHash(binaryPath)
|
hash, err := getFileHash(binaryPath)
|
||||||
@ -75,13 +75,13 @@ func verifyBinary(ctx *context.Context, binaryPath, attestationPath, versionTag
|
|||||||
func getFileHash(binaryPath string) (string, error) {
|
func getFileHash(binaryPath string) (string, error) {
|
||||||
binaryFile, err := os.Open(binaryPath)
|
binaryFile, err := os.Open(binaryPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to read binary for verification purposes: %v", err)
|
return "", fmt.Errorf("failed to read binary for verification purposes: %w", err)
|
||||||
}
|
}
|
||||||
defer binaryFile.Close()
|
defer binaryFile.Close()
|
||||||
|
|
||||||
hasher := sha256.New()
|
hasher := sha256.New()
|
||||||
if _, err := io.Copy(hasher, binaryFile); err != nil {
|
if _, err := io.Copy(hasher, binaryFile); err != nil {
|
||||||
return "", fmt.Errorf("failed to hash binary: %v", err)
|
return "", fmt.Errorf("failed to hash binary: %w", err)
|
||||||
}
|
}
|
||||||
hash := hex.EncodeToString(hasher.Sum(nil))
|
hash := hex.EncodeToString(hasher.Sum(nil))
|
||||||
return hash, nil
|
return hash, nil
|
||||||
@ -95,5 +95,5 @@ func handleSlsaFailure(srcErr error) error {
|
|||||||
fmt.Println("Proceeding with update...")
|
fmt.Println("Proceeding with update...")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return fmt.Errorf("failed to verify SLSA provenance of the updated binary, aborting update (to bypass, set `export HISHTORY_DISABLE_SLSA_ATTESTATION=true`): %v", srcErr)
|
return fmt.Errorf("failed to verify SLSA provenance of the updated binary, aborting update (to bypass, set `export HISHTORY_DISABLE_SLSA_ATTESTATION=true`): %w", srcErr)
|
||||||
}
|
}
|
||||||
|
@ -381,7 +381,7 @@ func getRows(ctx *context.Context, columnNames []string, query string, numEntrie
|
|||||||
entry.Command = strings.ReplaceAll(entry.Command, "\n", "\\n")
|
entry.Command = strings.ReplaceAll(entry.Command, "\n", "\\n")
|
||||||
row, err := buildTableRow(ctx, columnNames, *entry)
|
row, err := buildTableRow(ctx, columnNames, *entry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("failed to build row for entry=%#v: %v", entry, err)
|
return nil, nil, fmt.Errorf("failed to build row for entry=%#v: %w", entry, err)
|
||||||
}
|
}
|
||||||
rows = append(rows, row)
|
rows = append(rows, row)
|
||||||
filteredData = append(filteredData, entry)
|
filteredData = append(filteredData, entry)
|
||||||
@ -449,7 +449,7 @@ func makeTableColumns(ctx *context.Context, columnNames []string, rows []table.R
|
|||||||
// Get the actual terminal width. If we're below this, opportunistically add some padding aiming for the maximum column widths
|
// Get the actual terminal width. If we're below this, opportunistically add some padding aiming for the maximum column widths
|
||||||
terminalWidth, _, err := getTerminalSize()
|
terminalWidth, _, err := getTerminalSize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get terminal size: %v", err)
|
return nil, fmt.Errorf("failed to get terminal size: %w", err)
|
||||||
}
|
}
|
||||||
for totalWidth < (terminalWidth - len(columnNames)) {
|
for totalWidth < (terminalWidth - len(columnNames)) {
|
||||||
prevTotalWidth := totalWidth
|
prevTotalWidth := totalWidth
|
||||||
|
Loading…
x
Reference in New Issue
Block a user