mirror of
https://github.com/ddworken/hishtory.git
synced 2025-08-01 11:16:42 +02:00
125 lines
3.8 KiB
Go
125 lines
3.8 KiB
Go
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"os/user"
|
|
"time"
|
|
|
|
"github.com/ddworken/hishtory/client/data"
|
|
"github.com/ddworken/hishtory/client/hctx"
|
|
"github.com/ddworken/hishtory/client/lib"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var importCmd = &cobra.Command{
|
|
Use: "import",
|
|
GroupID: GROUP_ID_MANAGEMENT,
|
|
Hidden: true,
|
|
Short: "Re-import history entries from your existing shell history",
|
|
Long: "Note that you may also pipe commands to be imported in via stdin. For example `history | hishtory import`.",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
ctx := hctx.MakeContext()
|
|
numImported, err := lib.ImportHistory(ctx, true, true)
|
|
lib.CheckFatalError(err)
|
|
if numImported > 0 {
|
|
fmt.Printf("Imported %v history entries from your existing shell history\n", numImported)
|
|
}
|
|
},
|
|
}
|
|
|
|
var importJsonCmd = &cobra.Command{
|
|
Use: "import-json",
|
|
GroupID: GROUP_ID_MANAGEMENT,
|
|
Short: "Import history entries formatted in JSON lines format into hiSHtory",
|
|
Long: "Data is read from stdin. For example: `cat data.txt | hishtory import-json`.\n\nExample JSON format:\n\n```\n" +
|
|
"{\"command\":\"echo foo\"}\n" +
|
|
"{\"command\":\"echo bar\", \"current_working_directory\": \"/tmp/\"}\n" +
|
|
"{\"command\":\"ls\",\"current_working_directory\":\"/tmp/\",\"local_username\":\"david\",\"hostname\":\"foo\",\"home_directory\":\"/Users/david\",\"exit_code\":0,\"start_time\":\"2024-12-30T01:14:34.656407Z\",\"end_time\":\"2024-12-30T01:14:34.657407Z\"}\n```\n",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
ctx := hctx.MakeContext()
|
|
numImported, err := importFromJson(ctx)
|
|
lib.CheckFatalError(err)
|
|
fmt.Printf("Imported %v history entries\n", numImported)
|
|
},
|
|
}
|
|
|
|
func importFromJson(ctx context.Context) (int, error) {
|
|
// Get the data needed for filling in any missing columns
|
|
currentUser, err := user.Current()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
hostname, err := os.Hostname()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
homedir := hctx.GetHome(ctx)
|
|
|
|
// Build the entries
|
|
lines, err := lib.ReadStdin()
|
|
if err != nil {
|
|
return 0, fmt.Errorf("failed to read stdin for import: %w", err)
|
|
}
|
|
var entries []data.HistoryEntry
|
|
importEntryId := uuid.Must(uuid.NewRandom()).String()
|
|
importTimestamp := time.Now().UTC()
|
|
for i, line := range lines {
|
|
var entry data.HistoryEntry
|
|
err := json.Unmarshal([]byte(line), &entry)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("failed to parse JSON line %#v: %w", line, err)
|
|
}
|
|
if entry.Command == "" {
|
|
return 0, fmt.Errorf("cannot import history entries without a command, JSON line: %#v", line)
|
|
}
|
|
if entry.LocalUsername == "" {
|
|
entry.LocalUsername = currentUser.Username
|
|
}
|
|
if entry.Hostname == "" {
|
|
entry.Hostname = hostname
|
|
}
|
|
if entry.CurrentWorkingDirectory == "" {
|
|
entry.CurrentWorkingDirectory = "Unknown"
|
|
}
|
|
if entry.HomeDirectory == "" {
|
|
entry.HomeDirectory = homedir
|
|
}
|
|
// Set the timestamps so that they are monotonically increasing
|
|
startTime := importTimestamp.Add(time.Millisecond * time.Duration(i*2))
|
|
endTime := startTime.Add(time.Millisecond)
|
|
if entry.StartTime == *new(time.Time) {
|
|
entry.StartTime = startTime
|
|
}
|
|
if entry.EndTime == *new(time.Time) {
|
|
entry.EndTime = endTime
|
|
}
|
|
entry.DeviceId = hctx.GetConf(ctx).DeviceId
|
|
entry.EntryId = fmt.Sprintf("%s-%d", importEntryId, i)
|
|
entries = append(entries, entry)
|
|
}
|
|
|
|
// Insert the entries into the DB
|
|
db := hctx.GetDb(ctx)
|
|
err = db.CreateInBatches(entries, lib.ImportBatchSize).Error
|
|
if err != nil {
|
|
return 0, fmt.Errorf("failed to insert entries into DB: %w", err)
|
|
}
|
|
|
|
// Trigger a checkpoint so that these bulk entries are added from the WAL to the main DB
|
|
err = db.Exec("PRAGMA wal_checkpoint").Error
|
|
if err != nil {
|
|
return 0, fmt.Errorf("failed to checkpoint imported history: %w", err)
|
|
}
|
|
return len(entries), nil
|
|
}
|
|
|
|
func init() {
|
|
rootCmd.AddCommand(importCmd)
|
|
rootCmd.AddCommand(importJsonCmd)
|
|
}
|