mirror of
https://github.com/ddworken/hishtory.git
synced 2025-01-25 23:59:06 +01:00
refactoring, better tests, commit hash, banner, and tested working locally
This commit is contained in:
parent
684511f4f7
commit
1065fff062
8
Makefile
8
Makefile
@ -1,8 +1,10 @@
|
|||||||
test:
|
test:
|
||||||
HISHTORY_TEST=1 go test -p 1 ./...
|
HISHTORY_TEST=1 go test -p 1 ./...
|
||||||
|
|
||||||
build-static:
|
build-binary:
|
||||||
go build -o web/landing/www/binaries/hishtory-linux client/client.go
|
go build -o web/landing/www/binaries/hishtory-linux -ldflags "-X main.GitCommit=`git rev-list -1 HEAD`" client/client.go
|
||||||
|
|
||||||
|
build-static: build-binary
|
||||||
docker build -t gcr.io/dworken-k8s/hishtory-static -f web/caddy/Dockerfile .
|
docker build -t gcr.io/dworken-k8s/hishtory-static -f web/caddy/Dockerfile .
|
||||||
|
|
||||||
build-api:
|
build-api:
|
||||||
@ -15,3 +17,5 @@ deploy-static: build-static
|
|||||||
deploy-api: build-api
|
deploy-api: build-api
|
||||||
docker push gcr.io/dworken-k8s/hishtory-api
|
docker push gcr.io/dworken-k8s/hishtory-api
|
||||||
kubectl patch deployment hishtory-api -p "{\"spec\":{\"template\":{\"metadata\":{\"labels\":{\"ts\":\"`date|sed -e 's/ /_/g'|sed -e 's/:/-/g'`\"}}}}}}"
|
kubectl patch deployment hishtory-api -p "{\"spec\":{\"template\":{\"metadata\":{\"labels\":{\"ts\":\"`date|sed -e 's/ /_/g'|sed -e 's/:/-/g'`\"}}}}}}"
|
||||||
|
|
||||||
|
deploy: deploy-static deploy-api
|
||||||
|
@ -15,6 +15,8 @@ import (
|
|||||||
"github.com/ddworken/hishtory/shared"
|
"github.com/ddworken/hishtory/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var GitCommit string = "Unknown"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if len(os.Args) == 1 {
|
if len(os.Args) == 1 {
|
||||||
fmt.Println("Must specify a command! Do you mean `hishtory query`?")
|
fmt.Println("Must specify a command! Do you mean `hishtory query`?")
|
||||||
@ -38,8 +40,12 @@ func main() {
|
|||||||
case "status":
|
case "status":
|
||||||
config, err := lib.GetConfig()
|
config, err := lib.GetConfig()
|
||||||
lib.CheckFatalError(err)
|
lib.CheckFatalError(err)
|
||||||
fmt.Print("Hishtory: Offline Mode\nEnabled: ")
|
fmt.Print("Hishtory: e2e sync\nEnabled: ")
|
||||||
fmt.Print(config.IsEnabled)
|
fmt.Print(config.IsEnabled)
|
||||||
|
fmt.Print("\nSecret Key: ")
|
||||||
|
fmt.Print(config.UserSecret)
|
||||||
|
fmt.Print("\nCommit Hash: ")
|
||||||
|
fmt.Print(GitCommit)
|
||||||
fmt.Print("\n")
|
fmt.Print("\n")
|
||||||
case "update":
|
case "update":
|
||||||
lib.CheckFatalError(lib.Update("https://hishtory.dev/binaries/hishtory-linux"))
|
lib.CheckFatalError(lib.Update("https://hishtory.dev/binaries/hishtory-linux"))
|
||||||
@ -58,6 +64,9 @@ func retrieveAdditionalEntriesFromRemote(db *gorm.DB) error {
|
|||||||
return fmt.Errorf("failed to pull latest history entries from the backend: %v", err)
|
return fmt.Errorf("failed to pull latest history entries from the backend: %v", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return fmt.Errorf("failed to retrieve data from backend, status_code=%d", resp.StatusCode)
|
||||||
|
}
|
||||||
data, err := ioutil.ReadAll(resp.Body)
|
data, err := ioutil.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to read latest history entries response body: %v", err)
|
return fmt.Errorf("failed to read latest history entries response body: %v", err)
|
||||||
@ -72,21 +81,44 @@ func retrieveAdditionalEntriesFromRemote(db *gorm.DB) error {
|
|||||||
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: %v", err)
|
||||||
}
|
}
|
||||||
// TODO: Is this creating duplicate entries?
|
|
||||||
lib.AddToDbIfNew(db, decEntry)
|
lib.AddToDbIfNew(db, decEntry)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func query(query string) {
|
func query(query string) {
|
||||||
db, err := shared.OpenLocalSqliteDb()
|
db, err := lib.OpenLocalSqliteDb()
|
||||||
lib.CheckFatalError(err)
|
lib.CheckFatalError(err)
|
||||||
lib.CheckFatalError(retrieveAdditionalEntriesFromRemote(db))
|
lib.CheckFatalError(retrieveAdditionalEntriesFromRemote(db))
|
||||||
|
lib.CheckFatalError(displayBannerIfSet())
|
||||||
data, err := shared.Search(db, query, 25)
|
data, err := shared.Search(db, query, 25)
|
||||||
lib.CheckFatalError(err)
|
lib.CheckFatalError(err)
|
||||||
lib.DisplayResults(data, false)
|
lib.DisplayResults(data, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func displayBannerIfSet() error {
|
||||||
|
config, err := lib.GetConfig()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get config: %v", err)
|
||||||
|
}
|
||||||
|
url := lib.GetServerHostname() + "/api/v1/banner?commit_hash=" + GitCommit + "&device_id=" + config.DeviceId + "&forced_banner=" + os.Getenv("FORCED_BANNER")
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to call /api/v1/banner: %v", err)
|
||||||
|
}
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return fmt.Errorf("failed to call %s, status_code=%d", url, resp.StatusCode)
|
||||||
|
}
|
||||||
|
data, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read /api/v1/banner response body: %v", err)
|
||||||
|
}
|
||||||
|
if len(data) > 0 {
|
||||||
|
fmt.Printf(string(data))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func saveHistoryEntry() {
|
func saveHistoryEntry() {
|
||||||
config, err := lib.GetConfig()
|
config, err := lib.GetConfig()
|
||||||
lib.CheckFatalError(err)
|
lib.CheckFatalError(err)
|
||||||
@ -97,7 +129,7 @@ func saveHistoryEntry() {
|
|||||||
lib.CheckFatalError(err)
|
lib.CheckFatalError(err)
|
||||||
|
|
||||||
// Persist it locally
|
// Persist it locally
|
||||||
db, err := shared.OpenLocalSqliteDb()
|
db, err := lib.OpenLocalSqliteDb()
|
||||||
lib.CheckFatalError(err)
|
lib.CheckFatalError(err)
|
||||||
result := db.Create(entry)
|
result := db.Create(entry)
|
||||||
lib.CheckFatalError(result.Error)
|
lib.CheckFatalError(result.Error)
|
||||||
@ -107,12 +139,15 @@ func saveHistoryEntry() {
|
|||||||
lib.CheckFatalError(err)
|
lib.CheckFatalError(err)
|
||||||
jsonValue, err := json.Marshal([]shared.EncHistoryEntry{encEntry})
|
jsonValue, err := json.Marshal([]shared.EncHistoryEntry{encEntry})
|
||||||
lib.CheckFatalError(err)
|
lib.CheckFatalError(err)
|
||||||
_, err = http.Post(lib.GetServerHostname()+"/api/v1/esubmit", "application/json", bytes.NewBuffer(jsonValue))
|
resp, err := http.Post(lib.GetServerHostname()+"/api/v1/esubmit", "application/json", bytes.NewBuffer(jsonValue))
|
||||||
lib.CheckFatalError(err)
|
lib.CheckFatalError(err)
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
lib.CheckFatalError(fmt.Errorf("failed to submit result to backend, status_code=%d", resp.StatusCode))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func export() {
|
func export() {
|
||||||
db, err := shared.OpenLocalSqliteDb()
|
db, err := lib.OpenLocalSqliteDb()
|
||||||
lib.CheckFatalError(err)
|
lib.CheckFatalError(err)
|
||||||
lib.CheckFatalError(retrieveAdditionalEntriesFromRemote(db))
|
lib.CheckFatalError(retrieveAdditionalEntriesFromRemote(db))
|
||||||
data, err := shared.Search(db, "", 0)
|
data, err := shared.Search(db, "", 0)
|
||||||
@ -121,3 +156,4 @@ func export() {
|
|||||||
fmt.Println(entry)
|
fmt.Println(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,9 +4,11 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/ddworken/hishtory/shared"
|
"github.com/ddworken/hishtory/shared"
|
||||||
)
|
)
|
||||||
@ -48,19 +50,13 @@ func TestIntegrationWithNewDevice(t *testing.T) {
|
|||||||
gvm use go1.17
|
gvm use go1.17
|
||||||
cd /home/david/code/hishtory/
|
cd /home/david/code/hishtory/
|
||||||
go build -o /tmp/client client/client.go
|
go build -o /tmp/client client/client.go
|
||||||
/tmp/client install`)
|
/tmp/client install ` + userSecret)
|
||||||
match, err := regexp.MatchString(`Setting secret hishtory key to .*`, out)
|
match, err := regexp.MatchString(`Setting secret hishtory key to .*`, out)
|
||||||
shared.Check(t, err)
|
shared.Check(t, err)
|
||||||
if !match {
|
if !match {
|
||||||
t.Fatalf("unexpected output from install: %v", out)
|
t.Fatalf("unexpected output from install: %v", out)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the secret key to the previous secret key
|
|
||||||
out = RunInteractiveBashCommands(t, `hishtory init `+userSecret)
|
|
||||||
if !strings.Contains(out, "Setting secret hishtory key to "+userSecret) {
|
|
||||||
t.Fatalf("Failed to re-init with the user secret: %v", out)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Querying should show the history from the previous run
|
// Querying should show the history from the previous run
|
||||||
out = RunInteractiveBashCommands(t, "hishtory query")
|
out = RunInteractiveBashCommands(t, "hishtory query")
|
||||||
expected := []string{"echo thisisrecorded", "hishtory enable", "echo bar", "echo foo", "ls /foo", "ls /bar", "ls /a"}
|
expected := []string{"echo thisisrecorded", "hishtory enable", "echo bar", "echo foo", "ls /foo", "ls /bar", "ls /a"}
|
||||||
@ -97,6 +93,13 @@ func TestIntegrationWithNewDevice(t *testing.T) {
|
|||||||
t.Fatalf("unexpected output from install: %v", out)
|
t.Fatalf("unexpected output from install: %v", out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run a command that shouldn't be in the hishtory later on
|
||||||
|
RunInteractiveBashCommands(t, `echo notinthehistory`)
|
||||||
|
out = RunInteractiveBashCommands(t, "hishtory query")
|
||||||
|
if !strings.Contains(out, "echo notinthehistory") {
|
||||||
|
t.Fatalf("output is missing `echo notinthehistory`")
|
||||||
|
}
|
||||||
|
|
||||||
// Set the secret key to the previous secret key
|
// Set the secret key to the previous secret key
|
||||||
out = RunInteractiveBashCommands(t, `hishtory init `+userSecret)
|
out = RunInteractiveBashCommands(t, `hishtory init `+userSecret)
|
||||||
if !strings.Contains(out, "Setting secret hishtory key to "+userSecret) {
|
if !strings.Contains(out, "Setting secret hishtory key to "+userSecret) {
|
||||||
@ -114,6 +117,10 @@ func TestIntegrationWithNewDevice(t *testing.T) {
|
|||||||
t.Fatalf("output has %#v in it multiple times! out=%#v", item, out)
|
t.Fatalf("output has %#v in it multiple times! out=%#v", item, out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// But not from the previous account
|
||||||
|
if strings.Contains(out, "notinthehistory") {
|
||||||
|
t.Fatalf("output contains the unexpected item: notinthehistory")
|
||||||
|
}
|
||||||
|
|
||||||
RunInteractiveBashCommands(t, "echo mynewercommand")
|
RunInteractiveBashCommands(t, "echo mynewercommand")
|
||||||
out = RunInteractiveBashCommands(t, "hishtory query")
|
out = RunInteractiveBashCommands(t, "hishtory query")
|
||||||
@ -141,14 +148,22 @@ func testIntegration(t *testing.T) string {
|
|||||||
}
|
}
|
||||||
userSecret := matches[1]
|
userSecret := matches[1]
|
||||||
|
|
||||||
// TODO(ddworken): Test the status subcommand
|
// Test the status subcommand
|
||||||
out = RunInteractiveBashCommands(t, `
|
out = RunInteractiveBashCommands(t, `
|
||||||
hishtory status
|
hishtory status
|
||||||
`)
|
`)
|
||||||
if out != "Hishtory: Offline Mode\nEnabled: true\n" {
|
if out != fmt.Sprintf("Hishtory: e2e sync\nEnabled: true\nSecret Key: %s\nCommit Hash: Unknown\n", userSecret) {
|
||||||
t.Fatalf("status command has unexpected output: %#v", out)
|
t.Fatalf("status command has unexpected output: %#v", out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test the banner
|
||||||
|
os.Setenv("FORCED_BANNER", "HELLO_FROM_SERVER")
|
||||||
|
out = RunInteractiveBashCommands(t, `hishtory query`)
|
||||||
|
if !strings.Contains(out, "HELLO_FROM_SERVER") {
|
||||||
|
t.Fatalf("hishtory query didn't show the banner message! out=%#v", out)
|
||||||
|
}
|
||||||
|
os.Setenv("FORCED_BANNER", "")
|
||||||
|
|
||||||
// Test recording commands
|
// Test recording commands
|
||||||
out = RunInteractiveBashCommands(t, `
|
out = RunInteractiveBashCommands(t, `
|
||||||
ls /a
|
ls /a
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/driver/sqlite"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
@ -122,26 +123,40 @@ func Setup(args []string) error {
|
|||||||
}
|
}
|
||||||
fmt.Println("Setting secret hishtory key to " + string(userSecret))
|
fmt.Println("Setting secret hishtory key to " + string(userSecret))
|
||||||
|
|
||||||
|
// Create and set the config
|
||||||
var config ClientConfig
|
var config ClientConfig
|
||||||
config.UserSecret = userSecret
|
config.UserSecret = userSecret
|
||||||
config.IsEnabled = true
|
config.IsEnabled = true
|
||||||
config.DeviceId = uuid.Must(uuid.NewRandom()).String()
|
config.DeviceId = uuid.Must(uuid.NewRandom()).String()
|
||||||
|
|
||||||
err := SetConfig(config)
|
err := 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: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = http.Get(GetServerHostname() + "/api/v1/eregister?user_id=" + shared.UserId(userSecret) + "&device_id=" + config.DeviceId)
|
// Drop all existing data
|
||||||
|
db, err := OpenLocalSqliteDb()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to open DB: %v", err)
|
||||||
|
}
|
||||||
|
db.Exec("DELETE FROM history_entries")
|
||||||
|
|
||||||
|
// Bootstrap from remote date
|
||||||
|
resp, err := http.Get(GetServerHostname() + "/api/v1/eregister?user_id=" + shared.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: %v", err)
|
||||||
}
|
}
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return fmt.Errorf("failed to register device with backend, status_code=%d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := http.Get(GetServerHostname() + "/api/v1/ebootstrap?user_id=" + shared.UserId(userSecret) + "&device_id=" + config.DeviceId)
|
resp, err = http.Get(GetServerHostname() + "/api/v1/ebootstrap?user_id=" + shared.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: %v", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return fmt.Errorf("failed to bootstrap device with data from existing devices, status_code=%d", resp.StatusCode)
|
||||||
|
}
|
||||||
data, err := ioutil.ReadAll(resp.Body)
|
data, err := ioutil.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to read bootstrap response body: %v", err)
|
return fmt.Errorf("failed to read bootstrap response body: %v", err)
|
||||||
@ -151,10 +166,6 @@ func Setup(args []string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to load JSON response: %v", err)
|
return fmt.Errorf("failed to load JSON response: %v", err)
|
||||||
}
|
}
|
||||||
db, err := shared.OpenLocalSqliteDb()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to open DB: %v", err)
|
|
||||||
}
|
|
||||||
for _, entry := range retrievedEntries {
|
for _, entry := range retrievedEntries {
|
||||||
decEntry, err := shared.DecryptHistoryEntry(userSecret, *entry)
|
decEntry, err := shared.DecryptHistoryEntry(userSecret, *entry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -389,7 +400,7 @@ func Update(url string) error {
|
|||||||
}
|
}
|
||||||
err = syscall.Unlink(path.Join(homedir, shared.HISHTORY_PATH, "hishtory"))
|
err = syscall.Unlink(path.Join(homedir, shared.HISHTORY_PATH, "hishtory"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to unlink: %v", err)
|
return fmt.Errorf("Failed to unlink %s: %v", path.Join(homedir, shared.HISHTORY_PATH, "hishtory"), err)
|
||||||
}
|
}
|
||||||
cmd = exec.Command("/tmp/hishtory-client", "install")
|
cmd = exec.Command("/tmp/hishtory-client", "install")
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
@ -405,3 +416,28 @@ func GetServerHostname() string {
|
|||||||
}
|
}
|
||||||
return "https://api.hishtory.dev"
|
return "https://api.hishtory.dev"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func OpenLocalSqliteDb() (*gorm.DB, error) {
|
||||||
|
homedir, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get user's home directory: %v", err)
|
||||||
|
}
|
||||||
|
err = os.MkdirAll(path.Join(homedir, shared.HISHTORY_PATH), 0744)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create ~/.hishtory dir: %v", err)
|
||||||
|
}
|
||||||
|
db, err := gorm.Open(sqlite.Open(path.Join(homedir, shared.HISHTORY_PATH, shared.DB_PATH)), &gorm.Config{SkipDefaultTransaction: true})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to connect to the DB: %v", err)
|
||||||
|
}
|
||||||
|
tx, err := db.DB()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = tx.Ping()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
db.AutoMigrate(&shared.HistoryEntry{})
|
||||||
|
return db, nil
|
||||||
|
}
|
||||||
|
@ -70,3 +70,48 @@ func TestGetUserSecret(t *testing.T) {
|
|||||||
t.Fatalf("GetUserSecret() returned the same values for different setups! val=%v", secret1)
|
t.Fatalf("GetUserSecret() returned the same values for different setups! val=%v", secret1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func TestPersist(t *testing.T) {
|
||||||
|
defer shared.BackupAndRestore(t)()
|
||||||
|
db, err := OpenLocalSqliteDb()
|
||||||
|
shared.Check(t, err)
|
||||||
|
|
||||||
|
entry := shared.MakeFakeHistoryEntry("ls ~/")
|
||||||
|
db.Create(entry)
|
||||||
|
var historyEntries []*shared.HistoryEntry
|
||||||
|
result := db.Find(&historyEntries)
|
||||||
|
shared.Check(t, result.Error)
|
||||||
|
if len(historyEntries) != 1 {
|
||||||
|
t.Fatalf("DB has %d entries, expected 1!", len(historyEntries))
|
||||||
|
}
|
||||||
|
dbEntry := historyEntries[0]
|
||||||
|
if !shared.EntryEquals(entry, *dbEntry) {
|
||||||
|
t.Fatalf("DB data is different than input! \ndb =%#v \ninput=%#v", *dbEntry, entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSearch(t *testing.T) {
|
||||||
|
defer shared.BackupAndRestore(t)()
|
||||||
|
db, err := OpenLocalSqliteDb()
|
||||||
|
shared.Check(t, err)
|
||||||
|
|
||||||
|
// Insert data
|
||||||
|
entry1 := shared.MakeFakeHistoryEntry("ls /foo")
|
||||||
|
db.Create(entry1)
|
||||||
|
entry2 := shared.MakeFakeHistoryEntry("ls /bar")
|
||||||
|
db.Create(entry2)
|
||||||
|
|
||||||
|
// Search for data
|
||||||
|
results, err := shared.Search(db, "ls", 5)
|
||||||
|
shared.Check(t, err)
|
||||||
|
if len(results) != 2 {
|
||||||
|
t.Fatalf("Search() returned %d results, expected 2!", len(results))
|
||||||
|
}
|
||||||
|
if !shared.EntryEquals(*results[0], entry2) {
|
||||||
|
t.Fatalf("Search()[0]=%#v, expected: %#v", results[0], entry2)
|
||||||
|
}
|
||||||
|
if !shared.EntryEquals(*results[1], entry1) {
|
||||||
|
t.Fatalf("Search()[0]=%#v, expected: %#v", results[1], entry1)
|
||||||
|
}
|
||||||
|
}
|
@ -32,6 +32,7 @@ func apiESubmitHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
panic(fmt.Sprintf("body=%#v, err=%v", data, err))
|
panic(fmt.Sprintf("body=%#v, err=%v", data, err))
|
||||||
}
|
}
|
||||||
GLOBAL_DB.Where("user_id = ?")
|
GLOBAL_DB.Where("user_id = ?")
|
||||||
|
fmt.Printf("apiESubmitHandler: received request containg %d EncHistoryEntry\n", len(entries))
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
tx := GLOBAL_DB.Where("user_id = ?", entry.UserId)
|
tx := GLOBAL_DB.Where("user_id = ?", entry.UserId)
|
||||||
var devices []*shared.Device
|
var devices []*shared.Device
|
||||||
@ -42,9 +43,13 @@ func apiESubmitHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
if len(devices) == 0 {
|
if len(devices) == 0 {
|
||||||
panic(fmt.Errorf("Found no devices associated with user_id=%s, can't save history entry!", entry.UserId))
|
panic(fmt.Errorf("Found no devices associated with user_id=%s, can't save history entry!", entry.UserId))
|
||||||
}
|
}
|
||||||
|
fmt.Printf("apiESubmitHandler: Found %d devices\n", len(devices))
|
||||||
for _, device := range devices {
|
for _, device := range devices {
|
||||||
entry.DeviceId = device.DeviceId
|
entry.DeviceId = device.DeviceId
|
||||||
GLOBAL_DB.Create(&entry)
|
result := GLOBAL_DB.Create(&entry)
|
||||||
|
if result.Error != nil {
|
||||||
|
panic(result.Error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -61,6 +66,7 @@ func apiEQueryHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
panic(fmt.Errorf("DB query error: %v", result.Error))
|
panic(fmt.Errorf("DB query error: %v", result.Error))
|
||||||
}
|
}
|
||||||
|
fmt.Printf("apiEQueryHandler: Found %d entries\n", len(historyEntries))
|
||||||
resp, err := json.Marshal(historyEntries)
|
resp, err := json.Marshal(historyEntries)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@ -90,13 +96,20 @@ func apiERegisterHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
GLOBAL_DB.Create(&shared.Device{UserId: userId, DeviceId: deviceId})
|
GLOBAL_DB.Create(&shared.Device{UserId: userId, DeviceId: deviceId})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func apiBannerHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
commitHash := r.URL.Query().Get("commit_hash")
|
||||||
|
deviceId := r.URL.Query().Get("device_id")
|
||||||
|
forcedBanner := r.URL.Query().Get("forced_banner")
|
||||||
|
fmt.Printf("apiBannerHandler: commit_hash=%#v, device_id=%#v, forced_banner=%#v\n", commitHash, deviceId, forcedBanner)
|
||||||
|
w.Write([]byte(forcedBanner))
|
||||||
|
}
|
||||||
|
|
||||||
func OpenDB() (*gorm.DB, error) {
|
func OpenDB() (*gorm.DB, error) {
|
||||||
if shared.IsTestEnvironment() {
|
if shared.IsTestEnvironment() {
|
||||||
db, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{})
|
db, err := gorm.Open(sqlite.Open("file::memory:?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: %v", err)
|
||||||
}
|
}
|
||||||
db.AutoMigrate(&shared.HistoryEntry{})
|
|
||||||
db.AutoMigrate(&shared.EncHistoryEntry{})
|
db.AutoMigrate(&shared.EncHistoryEntry{})
|
||||||
db.AutoMigrate(&shared.Device{})
|
db.AutoMigrate(&shared.Device{})
|
||||||
return db, nil
|
return db, nil
|
||||||
@ -106,7 +119,6 @@ func OpenDB() (*gorm.DB, error) {
|
|||||||
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: %v", err)
|
||||||
}
|
}
|
||||||
db.AutoMigrate(&shared.HistoryEntry{})
|
|
||||||
db.AutoMigrate(&shared.EncHistoryEntry{})
|
db.AutoMigrate(&shared.EncHistoryEntry{})
|
||||||
db.AutoMigrate(&shared.Device{})
|
db.AutoMigrate(&shared.Device{})
|
||||||
return db, nil
|
return db, nil
|
||||||
@ -138,5 +150,6 @@ func main() {
|
|||||||
http.HandleFunc("/api/v1/equery", apiEQueryHandler)
|
http.HandleFunc("/api/v1/equery", apiEQueryHandler)
|
||||||
http.HandleFunc("/api/v1/ebootstrap", apiEBootstrapHandler)
|
http.HandleFunc("/api/v1/ebootstrap", apiEBootstrapHandler)
|
||||||
http.HandleFunc("/api/v1/eregister", apiERegisterHandler)
|
http.HandleFunc("/api/v1/eregister", apiERegisterHandler)
|
||||||
|
http.HandleFunc("/api/v1/banner", apiBannerHandler)
|
||||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@ func TestESubmitThenQuery(t *testing.T) {
|
|||||||
// Set up
|
// Set up
|
||||||
defer shared.BackupAndRestore(t)()
|
defer shared.BackupAndRestore(t)()
|
||||||
InitDB()
|
InitDB()
|
||||||
// shared.Check(t, shared.Setup([]string{}))
|
|
||||||
|
|
||||||
// Register a few devices
|
// Register a few devices
|
||||||
userId := shared.UserId("key")
|
userId := shared.UserId("key")
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -16,7 +15,6 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"gorm.io/driver/sqlite"
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -164,33 +162,6 @@ func IsTestEnvironment() bool {
|
|||||||
return os.Getenv("HISHTORY_TEST") != ""
|
return os.Getenv("HISHTORY_TEST") != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func OpenLocalSqliteDb() (*gorm.DB, error) {
|
|
||||||
homedir, err := os.UserHomeDir()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get user's home directory: %v", err)
|
|
||||||
}
|
|
||||||
err = os.MkdirAll(path.Join(homedir, HISHTORY_PATH), 0744)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to create ~/.hishtory dir: %v", err)
|
|
||||||
}
|
|
||||||
db, err := gorm.Open(sqlite.Open(path.Join(homedir, HISHTORY_PATH, DB_PATH)), &gorm.Config{SkipDefaultTransaction: true})
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to connect to the DB: %v", err)
|
|
||||||
}
|
|
||||||
tx, err := db.DB()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = tx.Ping()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
db.AutoMigrate(&HistoryEntry{})
|
|
||||||
db.AutoMigrate(&EncHistoryEntry{})
|
|
||||||
db.AutoMigrate(&Device{})
|
|
||||||
return db, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Search(db *gorm.DB, query string, limit int) ([]*HistoryEntry, error) {
|
func Search(db *gorm.DB, query string, limit int) ([]*HistoryEntry, error) {
|
||||||
tokens, err := tokenize(query)
|
tokens, err := tokenize(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -4,52 +4,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPersist(t *testing.T) {
|
|
||||||
defer BackupAndRestore(t)()
|
|
||||||
// Check(t, Setup([]string{}))
|
|
||||||
db, err := OpenLocalSqliteDb()
|
|
||||||
Check(t, err)
|
|
||||||
|
|
||||||
entry := MakeFakeHistoryEntry("ls ~/")
|
|
||||||
db.Create(entry)
|
|
||||||
var historyEntries []*HistoryEntry
|
|
||||||
result := db.Find(&historyEntries)
|
|
||||||
Check(t, result.Error)
|
|
||||||
if len(historyEntries) != 1 {
|
|
||||||
t.Fatalf("DB has %d entries, expected 1!", len(historyEntries))
|
|
||||||
}
|
|
||||||
dbEntry := historyEntries[0]
|
|
||||||
if !EntryEquals(entry, *dbEntry) {
|
|
||||||
t.Fatalf("DB data is different than input! \ndb =%#v \ninput=%#v", *dbEntry, entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSearch(t *testing.T) {
|
|
||||||
defer BackupAndRestore(t)()
|
|
||||||
// Check(t, Setup([]string{}))
|
|
||||||
db, err := OpenLocalSqliteDb()
|
|
||||||
Check(t, err)
|
|
||||||
|
|
||||||
// Insert data
|
|
||||||
entry1 := MakeFakeHistoryEntry("ls /foo")
|
|
||||||
db.Create(entry1)
|
|
||||||
entry2 := MakeFakeHistoryEntry("ls /bar")
|
|
||||||
db.Create(entry2)
|
|
||||||
|
|
||||||
// Search for data
|
|
||||||
results, err := Search(db, "ls", 5)
|
|
||||||
Check(t, err)
|
|
||||||
if len(results) != 2 {
|
|
||||||
t.Fatalf("Search() returned %d results, expected 2!", len(results))
|
|
||||||
}
|
|
||||||
if !EntryEquals(*results[0], entry2) {
|
|
||||||
t.Fatalf("Search()[0]=%#v, expected: %#v", results[0], entry2)
|
|
||||||
}
|
|
||||||
if !EntryEquals(*results[1], entry1) {
|
|
||||||
t.Fatalf("Search()[0]=%#v, expected: %#v", results[1], entry1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEncryptDecrypt(t *testing.T) {
|
func TestEncryptDecrypt(t *testing.T) {
|
||||||
k1, err := EncryptionKey("key")
|
k1, err := EncryptionKey("key")
|
||||||
Check(t, err)
|
Check(t, err)
|
||||||
|
Binary file not shown.
@ -103,13 +103,14 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div class="tab-pane active" id="install-online" role="tabpanel" aria-labelledby="home-tab">
|
<div class="tab-pane active" id="install-first" role="tabpanel" aria-labelledby="home-tab">
|
||||||
<br>To install hishtory in online mode on your first machine:
|
<br>To install hishtory on your first machine:
|
||||||
<br> <code>curl -o hishtory https://hishtory.dev/hishtory-online; chmod +x hishtory; ./hishtory install</code>
|
<br> <code>curl -o hishtory https://hishtory.dev/binaries/hishtory-linux; chmod +x hishtory; ./hishtory install</code>
|
||||||
<br> To install hishtory in online mode on your other machines:
|
|
||||||
<br> <code>curl -o hishtory https://hishtory.dev/hishtory-online; chmod +x hishtory; ./hishtory install $YOUR_HISHTORY_SECRET</code>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane" id="install-offline" role="tabpanel" aria-labelledby="profile-tab"><br> To install hishtory in offline mode: <br> <code>curl -o hishtory https://hishtory.dev/hishtory-offline; chmod +x hishtory; ./hishtory install</code>
|
<div class="tab-pane" id="install-offline" role="tabpanel" aria-labelledby="profile-tab">
|
||||||
|
<br>To install hishtory on your second machine, you must first retrieve your secret key from first first machine. To do so, run <code>hishtory status</code> and copy your "Secret Key".
|
||||||
|
<br> Then to install it on your second machine:
|
||||||
|
<br> <code>curl -o hishtory https://hishtory.dev/binaries/hishtory-linux; chmod +x hishtory; ./hishtory install $YOUR_HISHTORY_SECRET</code>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user