mirror of
https://github.com/ddworken/hishtory.git
synced 2025-02-16 18:41:03 +01:00
well, it builds :D but still failing all the tests
This commit is contained in:
parent
a4daa28e26
commit
e2acc6612a
3
Makefile
3
Makefile
@ -2,8 +2,7 @@ test:
|
|||||||
HISHTORY_TEST=1 go test -p 1 ./...
|
HISHTORY_TEST=1 go test -p 1 ./...
|
||||||
|
|
||||||
build-static:
|
build-static:
|
||||||
go build -o web/landing/www/hishtory-offline clients/local/client.go
|
go build -o web/landing/www/binaries/hishtory-linux client/client.go
|
||||||
go build -o web/landing/www/hishtory-online clients/remote/client.go
|
|
||||||
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:
|
||||||
|
88
client/client.go
Normal file
88
client/client.go
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ddworken/hishtory/shared"
|
||||||
|
"github.com/ddworken/hishtory/client/lib"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if len(os.Args) == 1 {
|
||||||
|
fmt.Println("Must specify a command! Do you mean `hishtory query`?")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch os.Args[1] {
|
||||||
|
case "saveHistoryEntry":
|
||||||
|
saveHistoryEntry()
|
||||||
|
case "query":
|
||||||
|
query(strings.Join(os.Args[2:], " "))
|
||||||
|
case "export":
|
||||||
|
export()
|
||||||
|
case "init":
|
||||||
|
lib.CheckFatalError(lib.Setup( os.Args))
|
||||||
|
case "install":
|
||||||
|
lib.CheckFatalError(lib.Install())
|
||||||
|
case "enable":
|
||||||
|
lib.CheckFatalError(lib.Enable())
|
||||||
|
case "disable":
|
||||||
|
lib.CheckFatalError(lib.Disable())
|
||||||
|
case "status":
|
||||||
|
config, err := lib.GetConfig()
|
||||||
|
lib.CheckFatalError(err)
|
||||||
|
fmt.Print("Hishtory: Offline Mode\nEnabled: ")
|
||||||
|
fmt.Print(config.IsEnabled)
|
||||||
|
fmt.Print("\n")
|
||||||
|
case "update":
|
||||||
|
lib.CheckFatalError(lib.Update("https://hishtory.dev/binaries/hishtory-linux"))
|
||||||
|
default:
|
||||||
|
lib.CheckFatalError(fmt.Errorf("unknown command: %s", os.Args[1]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func query(query string) {
|
||||||
|
db, err := shared.OpenLocalSqliteDb()
|
||||||
|
lib.CheckFatalError(err)
|
||||||
|
data, err := shared.Search(db, query, 25)
|
||||||
|
lib.CheckFatalError(err)
|
||||||
|
lib.DisplayResults(data, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveHistoryEntry() {
|
||||||
|
config, err := lib.GetConfig()
|
||||||
|
lib.CheckFatalError(err)
|
||||||
|
if !config.IsEnabled {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
entry, err := lib.BuildHistoryEntry(os.Args)
|
||||||
|
lib.CheckFatalError(err)
|
||||||
|
|
||||||
|
// Persist it locally
|
||||||
|
db, err := shared.OpenLocalSqliteDb()
|
||||||
|
lib.CheckFatalError(err)
|
||||||
|
result := db.Create(entry)
|
||||||
|
lib.CheckFatalError(result.Error)
|
||||||
|
|
||||||
|
// Persist it remotely
|
||||||
|
encEntry, err := shared.EncryptHistoryEntry(config.UserSecret, *entry)
|
||||||
|
lib.CheckFatalError(err)
|
||||||
|
jsonValue, err := json.Marshal(encEntry)
|
||||||
|
lib.CheckFatalError(err)
|
||||||
|
_, err = http.Post(lib.GetServerHostname()+"/api/v1/esubmit", "application/json", bytes.NewBuffer(jsonValue))
|
||||||
|
lib.CheckFatalError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func export() {
|
||||||
|
db, err := shared.OpenLocalSqliteDb()
|
||||||
|
lib.CheckFatalError(err)
|
||||||
|
data, err := shared.Search(db, "", 0)
|
||||||
|
lib.CheckFatalError(err)
|
||||||
|
for _, entry := range data {
|
||||||
|
fmt.Println(entry)
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package shared
|
package lib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
@ -19,14 +19,11 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/rodaine/table"
|
"github.com/rodaine/table"
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
"github.com/ddworken/hishtory/shared"
|
||||||
CONFIG_PATH = ".hishtory.config"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -52,8 +49,8 @@ func getCwd() (string, error) {
|
|||||||
return cwd, nil
|
return cwd, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func BuildHistoryEntry(args []string) (*HistoryEntry, error) {
|
func BuildHistoryEntry(args []string) (*shared.HistoryEntry, error) {
|
||||||
var entry HistoryEntry
|
var entry shared.HistoryEntry
|
||||||
|
|
||||||
// exitCode
|
// exitCode
|
||||||
exitCode, err := strconv.Atoi(args[2])
|
exitCode, err := strconv.Atoi(args[2])
|
||||||
@ -99,14 +96,7 @@ func BuildHistoryEntry(args []string) (*HistoryEntry, error) {
|
|||||||
return nil, fmt.Errorf("failed to build history entry: %v", err)
|
return nil, fmt.Errorf("failed to build history entry: %v", err)
|
||||||
}
|
}
|
||||||
entry.Hostname = hostname
|
entry.Hostname = hostname
|
||||||
|
|
||||||
// user secret
|
|
||||||
userSecret, err := GetUserSecret()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to build history entry: %v", err)
|
|
||||||
}
|
|
||||||
entry.UserSecret = userSecret
|
|
||||||
|
|
||||||
return &entry, nil
|
return &entry, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,14 +129,14 @@ func Setup(args []string) error {
|
|||||||
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)
|
||||||
_, 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)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func DisplayResults(results []*HistoryEntry, displayHostname bool) {
|
func DisplayResults(results []*shared.HistoryEntry, displayHostname bool) {
|
||||||
headerFmt := color.New(color.FgGreen, color.Underline).SprintfFunc()
|
headerFmt := color.New(color.FgGreen, color.Underline).SprintfFunc()
|
||||||
tbl := table.New("CWD", "Timestamp", "Runtime", "Exit Code", "Command")
|
tbl := table.New("CWD", "Timestamp", "Runtime", "Exit Code", "Command")
|
||||||
if displayHostname {
|
if displayHostname {
|
||||||
@ -178,7 +168,7 @@ func GetConfig() (ClientConfig, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return ClientConfig{}, fmt.Errorf("failed to retrieve homedir: %v", err)
|
return ClientConfig{}, fmt.Errorf("failed to retrieve homedir: %v", err)
|
||||||
}
|
}
|
||||||
data, err := os.ReadFile(path.Join(homedir, HISHTORY_PATH, CONFIG_PATH))
|
data, err := os.ReadFile(path.Join(homedir, shared.HISHTORY_PATH, shared.CONFIG_PATH))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ClientConfig{}, fmt.Errorf("failed to read config file: %v", err)
|
return ClientConfig{}, fmt.Errorf("failed to read config file: %v", err)
|
||||||
}
|
}
|
||||||
@ -199,12 +189,12 @@ func SetConfig(config ClientConfig) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to retrieve homedir: %v", err)
|
return fmt.Errorf("failed to retrieve homedir: %v", err)
|
||||||
}
|
}
|
||||||
clientDir := path.Join(homedir, HISHTORY_PATH)
|
clientDir := path.Join(homedir, shared.HISHTORY_PATH)
|
||||||
err = os.MkdirAll(clientDir, 0744)
|
err = os.MkdirAll(clientDir, 0744)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create ~/.hishtory/ folder: %v", err)
|
return fmt.Errorf("failed to create ~/.hishtory/ folder: %v", err)
|
||||||
}
|
}
|
||||||
err = os.WriteFile(path.Join(homedir, HISHTORY_PATH, CONFIG_PATH), serializedConfig, 0600)
|
err = os.WriteFile(path.Join(homedir, shared.HISHTORY_PATH, shared.CONFIG_PATH), serializedConfig, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to write config: %v", err)
|
return fmt.Errorf("failed to write config: %v", err)
|
||||||
}
|
}
|
||||||
@ -248,7 +238,7 @@ func Install() error {
|
|||||||
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: %v", err)
|
||||||
}
|
}
|
||||||
clientDir := path.Join(homedir, HISHTORY_PATH)
|
clientDir := path.Join(homedir, shared.HISHTORY_PATH)
|
||||||
err = os.MkdirAll(clientDir, 0744)
|
err = os.MkdirAll(clientDir, 0744)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create folder for hishtory binary: %v", err)
|
return fmt.Errorf("failed to create folder for hishtory binary: %v", err)
|
||||||
@ -300,7 +290,7 @@ func configureBashrc(homedir, binaryPath string) error {
|
|||||||
func installBinary(homedir string) (string, error) {
|
func installBinary(homedir string) (string, error) {
|
||||||
clientPath, err := exec.LookPath("hishtory")
|
clientPath, err := exec.LookPath("hishtory")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
clientPath = path.Join(homedir, HISHTORY_PATH, "hishtory")
|
clientPath = path.Join(homedir, shared.HISHTORY_PATH, "hishtory")
|
||||||
}
|
}
|
||||||
err = copyFile(os.Args[0], clientPath)
|
err = copyFile(os.Args[0], clientPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -350,3 +340,11 @@ func Update(url string) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func GetServerHostname() string {
|
||||||
|
if server := os.Getenv("HISHTORY_SERVER"); server != "" {
|
||||||
|
return server
|
||||||
|
}
|
||||||
|
return "http://localhost:8080"
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package shared
|
package lib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
@ -6,34 +6,33 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ddworken/hishtory/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSetup(t *testing.T) {
|
func TestSetup(t *testing.T) {
|
||||||
defer BackupAndRestore(t)()
|
defer shared.BackupAndRestore(t)()
|
||||||
homedir, err := os.UserHomeDir()
|
homedir, err := os.UserHomeDir()
|
||||||
Check(t, err)
|
shared.Check(t, err)
|
||||||
if _, err := os.Stat(path.Join(homedir, HISHTORY_PATH, CONFIG_PATH)); err == nil {
|
if _, err := os.Stat(path.Join(homedir, shared.HISHTORY_PATH, shared.CONFIG_PATH)); err == nil {
|
||||||
t.Fatalf("hishtory secret file already exists!")
|
t.Fatalf("hishtory secret file already exists!")
|
||||||
}
|
}
|
||||||
Check(t, Setup([]string{}))
|
shared.Check(t, Setup([]string{}))
|
||||||
if _, err := os.Stat(path.Join(homedir, HISHTORY_PATH, CONFIG_PATH)); err != nil {
|
if _, err := os.Stat(path.Join(homedir, shared.HISHTORY_PATH, shared.CONFIG_PATH)); err != nil {
|
||||||
t.Fatalf("hishtory secret file does not exist after Setup()!")
|
t.Fatalf("hishtory secret file does not exist after Setup()!")
|
||||||
}
|
}
|
||||||
data, err := os.ReadFile(path.Join(homedir, HISHTORY_PATH, CONFIG_PATH))
|
data, err := os.ReadFile(path.Join(homedir, shared.HISHTORY_PATH, shared.CONFIG_PATH))
|
||||||
Check(t, err)
|
shared.Check(t, err)
|
||||||
if len(data) < 10 {
|
if len(data) < 10 {
|
||||||
t.Fatalf("hishtory secret has unexpected length: %d", len(data))
|
t.Fatalf("hishtory secret has unexpected length: %d", len(data))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuildHistoryEntry(t *testing.T) {
|
func TestBuildHistoryEntry(t *testing.T) {
|
||||||
defer BackupAndRestore(t)()
|
defer shared.BackupAndRestore(t)()
|
||||||
Check(t, Setup([]string{}))
|
shared.Check(t, Setup([]string{}))
|
||||||
entry, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls / ", "1641774958326745663"})
|
entry, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls / ", "1641774958326745663"})
|
||||||
Check(t, err)
|
shared.Check(t, err)
|
||||||
if entry.UserSecret == "" || len(entry.UserSecret) < 10 || strings.TrimSpace(entry.UserSecret) != entry.UserSecret {
|
|
||||||
t.Fatalf("history entry has unexpected user secret: %v", entry.UserSecret)
|
|
||||||
}
|
|
||||||
if entry.ExitCode != 120 {
|
if entry.ExitCode != 120 {
|
||||||
t.Fatalf("history entry has unexpected exit code: %v", entry.ExitCode)
|
t.Fatalf("history entry has unexpected exit code: %v", entry.ExitCode)
|
||||||
}
|
}
|
||||||
@ -52,17 +51,17 @@ func TestBuildHistoryEntry(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetUserSecret(t *testing.T) {
|
func TestGetUserSecret(t *testing.T) {
|
||||||
defer BackupAndRestore(t)()
|
defer shared.BackupAndRestore(t)()
|
||||||
Check(t, Setup([]string{}))
|
shared.Check(t, Setup([]string{}))
|
||||||
secret1, err := GetUserSecret()
|
secret1, err := GetUserSecret()
|
||||||
Check(t, err)
|
shared.Check(t, err)
|
||||||
if len(secret1) < 10 || strings.Contains(secret1, " ") || strings.Contains(secret1, "\n") {
|
if len(secret1) < 10 || strings.Contains(secret1, " ") || strings.Contains(secret1, "\n") {
|
||||||
t.Fatalf("unexpected secret: %v", secret1)
|
t.Fatalf("unexpected secret: %v", secret1)
|
||||||
}
|
}
|
||||||
|
|
||||||
Check(t, Setup([]string{}))
|
shared.Check(t, Setup([]string{}))
|
||||||
secret2, err := GetUserSecret()
|
secret2, err := GetUserSecret()
|
||||||
Check(t, err)
|
shared.Check(t, err)
|
||||||
|
|
||||||
if secret1 == secret2 {
|
if secret1 == secret2 {
|
||||||
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)
|
@ -1,98 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ddworken/hishtory/shared"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
if len(os.Args) == 1 {
|
|
||||||
fmt.Println("Must specify a command! Do you mean `hishtory query`?")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch os.Args[1] {
|
|
||||||
case "saveHistoryEntry":
|
|
||||||
saveHistoryEntry()
|
|
||||||
case "query":
|
|
||||||
query(strings.Join(os.Args[2:], " "))
|
|
||||||
case "export":
|
|
||||||
export()
|
|
||||||
case "init":
|
|
||||||
shared.CheckFatalError(shared.Setup( os.Args))
|
|
||||||
case "install":
|
|
||||||
shared.CheckFatalError(shared.Install())
|
|
||||||
case "enable":
|
|
||||||
shared.CheckFatalError(shared.Enable())
|
|
||||||
case "disable":
|
|
||||||
shared.CheckFatalError(shared.Disable())
|
|
||||||
case "status":
|
|
||||||
config, err := shared.GetConfig()
|
|
||||||
shared.CheckFatalError(err)
|
|
||||||
fmt.Print("Hishtory: Offline Mode\nEnabled: ")
|
|
||||||
fmt.Print(config.IsEnabled)
|
|
||||||
fmt.Print("\n")
|
|
||||||
case "update":
|
|
||||||
shared.CheckFatalError(shared.Update("https://hishtory.dev/hishtory-offline"))
|
|
||||||
default:
|
|
||||||
shared.CheckFatalError(fmt.Errorf("unknown command: %s", os.Args[1]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getServerHostname() string {
|
|
||||||
if server := os.Getenv("HISHTORY_SERVER"); server != "" {
|
|
||||||
return server
|
|
||||||
}
|
|
||||||
return "http://localhost:8080"
|
|
||||||
}
|
|
||||||
|
|
||||||
func query(query string) {
|
|
||||||
userSecret, err := shared.GetUserSecret()
|
|
||||||
shared.CheckFatalError(err)
|
|
||||||
db, err := shared.OpenLocalSqliteDb()
|
|
||||||
shared.CheckFatalError(err)
|
|
||||||
data, err := shared.Search(db, userSecret, query, 25)
|
|
||||||
shared.CheckFatalError(err)
|
|
||||||
shared.DisplayResults(data, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func saveHistoryEntry() {
|
|
||||||
config, err := shared.GetConfig()
|
|
||||||
shared.CheckFatalError(err)
|
|
||||||
if !config.IsEnabled {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
entry, err := shared.BuildHistoryEntry(os.Args)
|
|
||||||
shared.CheckFatalError(err)
|
|
||||||
|
|
||||||
// Persist it locally
|
|
||||||
db, err := shared.OpenLocalSqliteDb()
|
|
||||||
shared.CheckFatalError(err)
|
|
||||||
result := db.Create(entry)
|
|
||||||
shared.CheckFatalError(result.Error)
|
|
||||||
|
|
||||||
// Persist it remotely
|
|
||||||
encEntry, err := shared.EncryptHistoryEntry(config.UserSecret, *entry)
|
|
||||||
shared.CheckFatalError(err)
|
|
||||||
jsonValue, err := json.Marshal(encEntry)
|
|
||||||
shared.CheckFatalError(err)
|
|
||||||
_, err = http.Post(getServerHostname()+"/api/v1/esubmit", "application/json", bytes.NewBuffer(jsonValue))
|
|
||||||
shared.CheckFatalError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func export() {
|
|
||||||
userSecret, err := shared.GetUserSecret()
|
|
||||||
shared.CheckFatalError(err)
|
|
||||||
db, err := shared.OpenLocalSqliteDb()
|
|
||||||
shared.CheckFatalError(err)
|
|
||||||
data, err := shared.Search(db, userSecret, "", 0)
|
|
||||||
shared.CheckFatalError(err)
|
|
||||||
for _, entry := range data {
|
|
||||||
fmt.Println(entry)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,125 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ddworken/hishtory/shared"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
if len(os.Args) == 1 {
|
|
||||||
fmt.Println("Must specify a command! Do you mean `hishtory query`?")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch os.Args[1] {
|
|
||||||
case "saveHistoryEntry":
|
|
||||||
saveHistoryEntry()
|
|
||||||
case "query":
|
|
||||||
query(strings.Join(os.Args[2:], " "))
|
|
||||||
case "export":
|
|
||||||
export()
|
|
||||||
case "init":
|
|
||||||
shared.CheckFatalError(shared.Setup(os.Args))
|
|
||||||
case "install":
|
|
||||||
shared.CheckFatalError(shared.Install())
|
|
||||||
// TODO: should be able to do hishtory install $secret
|
|
||||||
case "enable":
|
|
||||||
shared.CheckFatalError(shared.Enable())
|
|
||||||
case "disable":
|
|
||||||
shared.CheckFatalError(shared.Disable())
|
|
||||||
case "status":
|
|
||||||
config, err := shared.GetConfig()
|
|
||||||
shared.CheckFatalError(err)
|
|
||||||
fmt.Print("Hishtory: Online Mode\nEnabled: ")
|
|
||||||
fmt.Print(config.IsEnabled)
|
|
||||||
fmt.Print("\nSecret Key: " + config.UserSecret + "\n")
|
|
||||||
fmt.Print("Remote Server: " + getServerHostname() + "\n")
|
|
||||||
case "update":
|
|
||||||
shared.CheckFatalError(shared.Update("https://hishtory.dev/hishtory-online"))
|
|
||||||
default:
|
|
||||||
shared.CheckFatalError(fmt.Errorf("unknown command: %s", os.Args[1]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getServerHostname() string {
|
|
||||||
if server := os.Getenv("HISHTORY_SERVER"); server != "" {
|
|
||||||
return server
|
|
||||||
}
|
|
||||||
return "https://api.hishtory.dev"
|
|
||||||
}
|
|
||||||
|
|
||||||
func query(query string) {
|
|
||||||
data, err := doQuery(query)
|
|
||||||
shared.CheckFatalError(err)
|
|
||||||
shared.DisplayResults(data, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func doQuery(query string) ([]*shared.HistoryEntry, error) {
|
|
||||||
userSecret, err := shared.GetUserSecret()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
req, err := http.NewRequest("GET", getServerHostname()+"/api/v1/search", nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
q := req.URL.Query()
|
|
||||||
q.Add("query", query)
|
|
||||||
q.Add("user_secret", userSecret)
|
|
||||||
q.Add("limit", "25")
|
|
||||||
req.URL.RawQuery = q.Encode()
|
|
||||||
|
|
||||||
client := &http.Client{}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
resp_body, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if resp.Status != "200 OK" {
|
|
||||||
return nil, fmt.Errorf("search API returned invalid result. status=" + resp.Status)
|
|
||||||
}
|
|
||||||
|
|
||||||
var data []*shared.HistoryEntry
|
|
||||||
err = json.Unmarshal(resp_body, &data)
|
|
||||||
return data, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func saveHistoryEntry() {
|
|
||||||
isEnabled, err := shared.IsEnabled()
|
|
||||||
shared.CheckFatalError(err)
|
|
||||||
if !isEnabled {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
entry, err := shared.BuildHistoryEntry(os.Args)
|
|
||||||
shared.CheckFatalError(err)
|
|
||||||
err = send(*entry)
|
|
||||||
shared.CheckFatalError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func send(entry shared.HistoryEntry) error {
|
|
||||||
jsonValue, err := json.Marshal(entry)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to marshal HistoryEntry as json: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = http.Post(getServerHostname()+"/api/v1/submit", "application/json", bytes.NewBuffer(jsonValue))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to send HistoryEntry to api: %v", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func export() {
|
|
||||||
// TODO(ddworken)
|
|
||||||
}
|
|
@ -1,89 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"os/exec"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ddworken/hishtory/shared"
|
|
||||||
)
|
|
||||||
|
|
||||||
func RunInteractiveBashCommands(t *testing.T, script string) string {
|
|
||||||
cmd := exec.Command("bash", "-i")
|
|
||||||
cmd.Stdin = strings.NewReader("export HISHTORY_SERVER=http://localhost:8080; /tmp/server &; sleep 2; " + script + "\nsleep 2; killall server || true;")
|
|
||||||
var out bytes.Buffer
|
|
||||||
cmd.Stdout = &out
|
|
||||||
var err bytes.Buffer
|
|
||||||
cmd.Stderr = &err
|
|
||||||
shared.CheckWithInfo(t, cmd.Run(), out.String()+err.String())
|
|
||||||
return out.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIntegration(t *testing.T) {
|
|
||||||
// Set up
|
|
||||||
defer shared.BackupAndRestore(t)()
|
|
||||||
|
|
||||||
// TODO(ddworken): Test status
|
|
||||||
|
|
||||||
// Test install
|
|
||||||
out := RunInteractiveBashCommands(t, `
|
|
||||||
gvm use go1.17
|
|
||||||
cd ../../
|
|
||||||
go build -o /tmp/client clients/remote/client.go
|
|
||||||
go build -o /tmp/server server/server.go
|
|
||||||
/tmp/client install`)
|
|
||||||
match, err := regexp.MatchString(`Setting secret hishtory key to .*`, out)
|
|
||||||
shared.Check(t, err)
|
|
||||||
if !match {
|
|
||||||
t.Fatalf("unexpected output from install: %v", out)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test recording commands
|
|
||||||
out = RunInteractiveBashCommands(t, `
|
|
||||||
ls /a
|
|
||||||
ls /bar
|
|
||||||
ls /foo
|
|
||||||
echo foo
|
|
||||||
echo bar
|
|
||||||
hishtory disable
|
|
||||||
echo thisisnotrecorded
|
|
||||||
hishtory enable
|
|
||||||
echo thisisrecorded
|
|
||||||
`)
|
|
||||||
if out != "foo\nbar\nthisisnotrecorded\nthisisrecorded\n" {
|
|
||||||
t.Fatalf("unexpected output from running commands: %#v", out)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test querying for all commands
|
|
||||||
out = RunInteractiveBashCommands(t, `hishtory query`)
|
|
||||||
expected := []string{"echo thisisrecorded", "hishtory enable", "echo bar", "echo foo", "ls /foo", "ls /bar", "ls /a", "ms"}
|
|
||||||
for _, item := range expected {
|
|
||||||
if !strings.Contains(out, item) {
|
|
||||||
t.Fatalf("output is missing expected item %#v: %#v", item, out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// match, err = regexp.MatchString(`.*[a-zA-Z-_0-9]+\s+~/.*\s+[a-zA-Z]{3} \d+ 2022 \d\d:\d\d:\d\d PST\s+\d{1,2}ms\s+0\s+echo thisisrecorded.*`, out)
|
|
||||||
// shared.Check(t, err)
|
|
||||||
// if !match {
|
|
||||||
// t.Fatalf("output is missing the row for `echo thisisrecorded`: %v", out)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Test querying for a specific command
|
|
||||||
out = RunInteractiveBashCommands(t, "hishtory query foo")
|
|
||||||
expected = []string{"echo foo", "ls /foo", "ms"}
|
|
||||||
unexpected := []string{"echo thisisrecorded", "hishtory enable", "echo bar", "ls /bar", "ls /a"}
|
|
||||||
for _, item := range expected {
|
|
||||||
if !strings.Contains(out, item) {
|
|
||||||
t.Fatalf("output is missing expected item %#v: %#v", item, out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, item := range unexpected {
|
|
||||||
if strings.Contains(out, item) {
|
|
||||||
t.Fatalf("output is containing unexpected item %#v: %#v", item, out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(ddworken): Test export
|
|
@ -5,7 +5,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/ddworken/hishtory/shared"
|
"github.com/ddworken/hishtory/shared"
|
||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
@ -19,16 +18,6 @@ const (
|
|||||||
|
|
||||||
var GLOBAL_DB *gorm.DB
|
var GLOBAL_DB *gorm.DB
|
||||||
|
|
||||||
func apiSubmitHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
decoder := json.NewDecoder(r.Body)
|
|
||||||
var entry shared.HistoryEntry
|
|
||||||
err := decoder.Decode(&entry)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
GLOBAL_DB.Create(&entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
func apiESubmitHandler(w http.ResponseWriter, r *http.Request) {
|
func apiESubmitHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
decoder := json.NewDecoder(r.Body)
|
decoder := json.NewDecoder(r.Body)
|
||||||
var entries []shared.EncHistoryEntry
|
var entries []shared.EncHistoryEntry
|
||||||
@ -109,29 +98,6 @@ func OpenDB() (*gorm.DB, error) {
|
|||||||
return db, nil
|
return db, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiSearchHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
userSecret := r.URL.Query().Get("user_secret")
|
|
||||||
query := r.URL.Query().Get("query")
|
|
||||||
fmt.Println("Received search query: " + query)
|
|
||||||
limitStr := r.URL.Query().Get("limit")
|
|
||||||
limit, err := strconv.Atoi(limitStr)
|
|
||||||
if err != nil {
|
|
||||||
limit = 0
|
|
||||||
}
|
|
||||||
entries, err := shared.Search(GLOBAL_DB, userSecret, query, limit)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
for _, entry := range entries {
|
|
||||||
entry.UserSecret = ""
|
|
||||||
}
|
|
||||||
resp, err := json.Marshal(entries)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
w.Write(resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
InitDB()
|
InitDB()
|
||||||
}
|
}
|
||||||
@ -154,8 +120,6 @@ func InitDB() {
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
fmt.Println("Listening on localhost:8080")
|
fmt.Println("Listening on localhost:8080")
|
||||||
http.HandleFunc("/api/v1/submit", apiSubmitHandler)
|
|
||||||
http.HandleFunc("/api/v1/search", apiSearchHandler)
|
|
||||||
http.HandleFunc("/api/v1/esubmit", apiESubmitHandler)
|
http.HandleFunc("/api/v1/esubmit", apiESubmitHandler)
|
||||||
http.HandleFunc("/api/v1/equery", apiEQueryHandler)
|
http.HandleFunc("/api/v1/equery", apiEQueryHandler)
|
||||||
http.HandleFunc("/api/v1/ebootstrap", apiEBootstrapHandler)
|
http.HandleFunc("/api/v1/ebootstrap", apiEBootstrapHandler)
|
||||||
|
@ -12,129 +12,11 @@ import (
|
|||||||
"github.com/ddworken/hishtory/shared"
|
"github.com/ddworken/hishtory/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSubmitThenQuery(t *testing.T) {
|
|
||||||
// Set up
|
|
||||||
defer shared.BackupAndRestore(t)()
|
|
||||||
InitDB()
|
|
||||||
shared.Check(t, shared.Setup([]string{}))
|
|
||||||
|
|
||||||
// Submit an entry
|
|
||||||
entry, err := shared.BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls / ", "1641774958326745663"})
|
|
||||||
shared.Check(t, err)
|
|
||||||
reqBody, err := json.Marshal(entry)
|
|
||||||
shared.Check(t, err)
|
|
||||||
submitReq := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBody))
|
|
||||||
apiSubmitHandler(nil, submitReq)
|
|
||||||
// Also submit one for another user
|
|
||||||
otherEntry := *entry
|
|
||||||
otherEntry.UserSecret = "aaaaaaaaa"
|
|
||||||
otherEntry.Command = "other"
|
|
||||||
reqBody, err = json.Marshal(otherEntry)
|
|
||||||
shared.Check(t, err)
|
|
||||||
submitReq = httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBody))
|
|
||||||
apiSubmitHandler(nil, submitReq)
|
|
||||||
|
|
||||||
// Retrieve the entry
|
|
||||||
secret, err := shared.GetUserSecret()
|
|
||||||
shared.Check(t, err)
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
searchReq := httptest.NewRequest(http.MethodGet, "/?user_secret="+secret, nil)
|
|
||||||
apiSearchHandler(w, searchReq)
|
|
||||||
res := w.Result()
|
|
||||||
defer res.Body.Close()
|
|
||||||
data, err := ioutil.ReadAll(res.Body)
|
|
||||||
shared.Check(t, err)
|
|
||||||
var retrievedEntries []*shared.HistoryEntry
|
|
||||||
shared.Check(t, json.Unmarshal(data, &retrievedEntries))
|
|
||||||
if len(retrievedEntries) != 1 {
|
|
||||||
t.Fatalf("Expected to retrieve 1 entry, found %d", len(retrievedEntries))
|
|
||||||
}
|
|
||||||
dbEntry := retrievedEntries[0]
|
|
||||||
if dbEntry.UserSecret != "" {
|
|
||||||
t.Fatalf("Response contains a user secret: %#v", *dbEntry)
|
|
||||||
}
|
|
||||||
entry.UserSecret = ""
|
|
||||||
if !shared.EntryEquals(*dbEntry, *entry) {
|
|
||||||
t.Fatalf("DB data is different than input! \ndb =%#v\ninput=%#v", *dbEntry, *entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNoUserSecretGivesNoResults(t *testing.T) {
|
|
||||||
// Set up
|
|
||||||
defer shared.BackupAndRestore(t)()
|
|
||||||
InitDB()
|
|
||||||
shared.Check(t, shared.Setup([]string{}))
|
|
||||||
|
|
||||||
// Submit an entry
|
|
||||||
entry, err := shared.BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls / ", "1641774958326745663"})
|
|
||||||
shared.Check(t, err)
|
|
||||||
reqBody, err := json.Marshal(entry)
|
|
||||||
shared.Check(t, err)
|
|
||||||
submitReq := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBody))
|
|
||||||
apiSubmitHandler(nil, submitReq)
|
|
||||||
|
|
||||||
// Retrieve entries with no user secret
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
searchReq := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
||||||
apiSearchHandler(w, searchReq)
|
|
||||||
res := w.Result()
|
|
||||||
defer res.Body.Close()
|
|
||||||
data, err := ioutil.ReadAll(res.Body)
|
|
||||||
shared.Check(t, err)
|
|
||||||
var retrievedEntries []*shared.HistoryEntry
|
|
||||||
shared.Check(t, json.Unmarshal(data, &retrievedEntries))
|
|
||||||
if len(retrievedEntries) != 0 {
|
|
||||||
t.Fatalf("Expected to retrieve 0 entries, found %d, results[0]=%#v", len(retrievedEntries), retrievedEntries[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSearchQuery(t *testing.T) {
|
|
||||||
// Set up
|
|
||||||
defer shared.BackupAndRestore(t)()
|
|
||||||
InitDB()
|
|
||||||
shared.Check(t, shared.Setup([]string{}))
|
|
||||||
|
|
||||||
// Submit an entry that we'll match
|
|
||||||
entry, err := shared.BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls /bar ", "1641774958326745663"})
|
|
||||||
shared.Check(t, err)
|
|
||||||
reqBody, err := json.Marshal(entry)
|
|
||||||
shared.Check(t, err)
|
|
||||||
submitReq := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBody))
|
|
||||||
apiSubmitHandler(nil, submitReq)
|
|
||||||
// Submit an entry that we won't match
|
|
||||||
entry, err = shared.BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls /foo ", "1641774958326745663"})
|
|
||||||
shared.Check(t, err)
|
|
||||||
reqBody, err = json.Marshal(entry)
|
|
||||||
shared.Check(t, err)
|
|
||||||
submitReq = httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBody))
|
|
||||||
apiSubmitHandler(nil, submitReq)
|
|
||||||
|
|
||||||
// Retrieve the entry
|
|
||||||
secret, err := shared.GetUserSecret()
|
|
||||||
shared.Check(t, err)
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
searchReq := httptest.NewRequest(http.MethodGet, "/?user_secret="+secret+"&query=foo", nil)
|
|
||||||
apiSearchHandler(w, searchReq)
|
|
||||||
res := w.Result()
|
|
||||||
defer res.Body.Close()
|
|
||||||
data, err := ioutil.ReadAll(res.Body)
|
|
||||||
shared.Check(t, err)
|
|
||||||
var retrievedEntries []*shared.HistoryEntry
|
|
||||||
shared.Check(t, json.Unmarshal(data, &retrievedEntries))
|
|
||||||
if len(retrievedEntries) != 1 {
|
|
||||||
t.Fatalf("Expected to retrieve 1 entry, found %d", len(retrievedEntries))
|
|
||||||
}
|
|
||||||
dbEntry := retrievedEntries[0]
|
|
||||||
if dbEntry.Command != "ls /foo" {
|
|
||||||
t.Fatalf("Response contains an unexpected command: %#v", *dbEntry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestESubmitThenQuery(t *testing.T) {
|
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{}))
|
// shared.Check(t, shared.Setup([]string{}))
|
||||||
|
|
||||||
// Register a few devices
|
// Register a few devices
|
||||||
userId := shared.UserId("key")
|
userId := shared.UserId("key")
|
||||||
@ -150,9 +32,8 @@ func TestESubmitThenQuery(t *testing.T) {
|
|||||||
apiERegisterHandler(nil, deviceReq)
|
apiERegisterHandler(nil, deviceReq)
|
||||||
|
|
||||||
// Submit a few entries for different devices
|
// Submit a few entries for different devices
|
||||||
entry, err := shared.BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls / ", "1641774958326745663"})
|
entry := shared.MakeFakeHistoryEntry("ls ~/")
|
||||||
shared.Check(t, err)
|
encEntry, err := shared.EncryptHistoryEntry("key", entry)
|
||||||
encEntry, err := shared.EncryptHistoryEntry("key", *entry)
|
|
||||||
shared.Check(t, err)
|
shared.Check(t, err)
|
||||||
reqBody, err := json.Marshal([]shared.EncHistoryEntry{encEntry})
|
reqBody, err := json.Marshal([]shared.EncHistoryEntry{encEntry})
|
||||||
shared.Check(t, err)
|
shared.Check(t, err)
|
||||||
@ -184,8 +65,8 @@ func TestESubmitThenQuery(t *testing.T) {
|
|||||||
}
|
}
|
||||||
decEntry, err := shared.DecryptHistoryEntry("key", *dbEntry)
|
decEntry, err := shared.DecryptHistoryEntry("key", *dbEntry)
|
||||||
shared.Check(t, err)
|
shared.Check(t, err)
|
||||||
if !shared.EntryEquals(decEntry, *entry) {
|
if !shared.EntryEquals(decEntry, entry) {
|
||||||
t.Fatalf("DB data is different than input! \ndb =%#v\ninput=%#v", *dbEntry, *entry)
|
t.Fatalf("DB data is different than input! \ndb =%#v\ninput=%#v", *dbEntry, entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Same for device id 2
|
// Same for device id 2
|
||||||
@ -212,8 +93,8 @@ func TestESubmitThenQuery(t *testing.T) {
|
|||||||
}
|
}
|
||||||
decEntry, err = shared.DecryptHistoryEntry("key", *dbEntry)
|
decEntry, err = shared.DecryptHistoryEntry("key", *dbEntry)
|
||||||
shared.Check(t, err)
|
shared.Check(t, err)
|
||||||
if !shared.EntryEquals(decEntry, *entry) {
|
if !shared.EntryEquals(decEntry, entry) {
|
||||||
t.Fatalf("DB data is different than input! \ndb =%#v\ninput=%#v", *dbEntry, *entry)
|
t.Fatalf("DB data is different than input! \ndb =%#v\ninput=%#v", *dbEntry, entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bootstrap handler should return 2 entries, one for each device
|
// Bootstrap handler should return 2 entries, one for each device
|
||||||
|
@ -21,8 +21,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type HistoryEntry struct {
|
type HistoryEntry struct {
|
||||||
// TODO: UserSecret needs to be removed from here once I drop all the old code
|
|
||||||
UserSecret string `json:"user_secret" gorm:"index"`
|
|
||||||
LocalUsername string `json:"local_username"`
|
LocalUsername string `json:"local_username"`
|
||||||
Hostname string `json:"hostname"`
|
Hostname string `json:"hostname"`
|
||||||
Command string `json:"command"`
|
Command string `json:"command"`
|
||||||
@ -56,6 +54,7 @@ type Device struct {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
CONFIG_PATH = ".hishtory.config"
|
||||||
HISHTORY_PATH = ".hishtory"
|
HISHTORY_PATH = ".hishtory"
|
||||||
DB_PATH = ".hishtory.db"
|
DB_PATH = ".hishtory.db"
|
||||||
KDF_USER_ID = "user_id"
|
KDF_USER_ID = "user_id"
|
||||||
@ -192,17 +191,18 @@ func OpenLocalSqliteDb() (*gorm.DB, error) {
|
|||||||
return db, nil
|
return db, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: DELETE THIS METHOD
|
||||||
func Persist(db *gorm.DB, entry HistoryEntry) error {
|
func Persist(db *gorm.DB, entry HistoryEntry) error {
|
||||||
db.Create(&entry)
|
db.Create(&entry)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Search(db *gorm.DB, userSecret, 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 {
|
||||||
return nil, fmt.Errorf("failed to tokenize query: %v", err)
|
return nil, fmt.Errorf("failed to tokenize query: %v", err)
|
||||||
}
|
}
|
||||||
tx := db.Where("user_secret = ?", userSecret)
|
tx := db.Where("true")
|
||||||
for _, token := range tokens {
|
for _, token := range tokens {
|
||||||
if strings.Contains(token, ":") {
|
if strings.Contains(token, ":") {
|
||||||
splitToken := strings.SplitN(token, ":", 2)
|
splitToken := strings.SplitN(token, ":", 2)
|
||||||
|
@ -6,13 +6,12 @@ import (
|
|||||||
|
|
||||||
func TestPersist(t *testing.T) {
|
func TestPersist(t *testing.T) {
|
||||||
defer BackupAndRestore(t)()
|
defer BackupAndRestore(t)()
|
||||||
Check(t, Setup([]string{}))
|
// Check(t, Setup([]string{}))
|
||||||
db, err := OpenLocalSqliteDb()
|
db, err := OpenLocalSqliteDb()
|
||||||
Check(t, err)
|
Check(t, err)
|
||||||
|
|
||||||
entry, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls / ", "1641774958326745663"})
|
entry := MakeFakeHistoryEntry("ls ~/")
|
||||||
Check(t, err)
|
Check(t, Persist(db, entry))
|
||||||
Check(t, Persist(db, *entry))
|
|
||||||
var historyEntries []*HistoryEntry
|
var historyEntries []*HistoryEntry
|
||||||
result := db.Find(&historyEntries)
|
result := db.Find(&historyEntries)
|
||||||
Check(t, result.Error)
|
Check(t, result.Error)
|
||||||
@ -20,40 +19,33 @@ func TestPersist(t *testing.T) {
|
|||||||
t.Fatalf("DB has %d entries, expected 1!", len(historyEntries))
|
t.Fatalf("DB has %d entries, expected 1!", len(historyEntries))
|
||||||
}
|
}
|
||||||
dbEntry := historyEntries[0]
|
dbEntry := historyEntries[0]
|
||||||
if !EntryEquals(*entry, *dbEntry) {
|
if !EntryEquals(entry, *dbEntry) {
|
||||||
t.Fatalf("DB data is different than input! \ndb =%#v \ninput=%#v", *dbEntry, *entry)
|
t.Fatalf("DB data is different than input! \ndb =%#v \ninput=%#v", *dbEntry, entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSearch(t *testing.T) {
|
func TestSearch(t *testing.T) {
|
||||||
defer BackupAndRestore(t)()
|
defer BackupAndRestore(t)()
|
||||||
Check(t, Setup([]string{}))
|
// Check(t, Setup([]string{}))
|
||||||
db, err := OpenLocalSqliteDb()
|
db, err := OpenLocalSqliteDb()
|
||||||
Check(t, err)
|
Check(t, err)
|
||||||
|
|
||||||
// Insert data
|
// Insert data
|
||||||
entry1, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls / ", "1641774958326745663"})
|
entry1 := MakeFakeHistoryEntry("ls /foo")
|
||||||
Check(t, err)
|
Check(t, Persist(db, entry1))
|
||||||
Check(t, Persist(db, *entry1))
|
entry2 := MakeFakeHistoryEntry("ls /bar")
|
||||||
entry2, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls /foo ", "1641774958326745663"})
|
Check(t, Persist(db, entry2))
|
||||||
Check(t, err)
|
|
||||||
Check(t, Persist(db, *entry2))
|
|
||||||
entry3, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 echo hi ", "1641774958326745663"})
|
|
||||||
Check(t, err)
|
|
||||||
Check(t, Persist(db, *entry3))
|
|
||||||
|
|
||||||
// Search for data
|
// Search for data
|
||||||
secret, err := GetUserSecret()
|
results, err := Search(db, "ls", 5)
|
||||||
Check(t, err)
|
|
||||||
results, err := Search(db, secret, "ls", 5)
|
|
||||||
Check(t, err)
|
Check(t, err)
|
||||||
if len(results) != 2 {
|
if len(results) != 2 {
|
||||||
t.Fatalf("Search() returned %d results, expected 2!", len(results))
|
t.Fatalf("Search() returned %d results, expected 2!", len(results))
|
||||||
}
|
}
|
||||||
if !EntryEquals(*results[0], *entry2) {
|
if !EntryEquals(*results[0], entry2) {
|
||||||
t.Fatalf("Search()[0]=%#v, expected: %#v", results[0], entry2)
|
t.Fatalf("Search()[0]=%#v, expected: %#v", results[0], entry2)
|
||||||
}
|
}
|
||||||
if !EntryEquals(*results[1], *entry1) {
|
if !EntryEquals(*results[1], entry1) {
|
||||||
t.Fatalf("Search()[0]=%#v, expected: %#v", results[1], entry1)
|
t.Fatalf("Search()[0]=%#v, expected: %#v", results[1], entry1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,12 @@
|
|||||||
package shared
|
package shared
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Check(t *testing.T, err error) {
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func CheckWithInfo(t *testing.T, err error, additionalInfo string) {
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected error: %v! Additional info: %v", err, additionalInfo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BackupAndRestore(t *testing.T) func() {
|
func BackupAndRestore(t *testing.T) func() {
|
||||||
homedir, err := os.UserHomeDir()
|
homedir, err := os.UserHomeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -33,14 +21,36 @@ func BackupAndRestore(t *testing.T) func() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Check(t *testing.T, err error) {
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckWithInfo(t *testing.T, err error, additionalInfo string) {
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error: %v! Additional info: %v", err, additionalInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func EntryEquals(entry1, entry2 HistoryEntry) bool {
|
func EntryEquals(entry1, entry2 HistoryEntry) bool {
|
||||||
return entry1.UserSecret == entry2.UserSecret &&
|
return entry1.LocalUsername == entry2.LocalUsername &&
|
||||||
entry1.LocalUsername == entry2.LocalUsername &&
|
|
||||||
entry1.Hostname == entry2.Hostname &&
|
entry1.Hostname == entry2.Hostname &&
|
||||||
entry1.Command == entry2.Command &&
|
entry1.Command == entry2.Command &&
|
||||||
entry1.CurrentWorkingDirectory == entry2.CurrentWorkingDirectory &&
|
entry1.CurrentWorkingDirectory == entry2.CurrentWorkingDirectory &&
|
||||||
entry1.ExitCode == entry2.ExitCode &&
|
entry1.ExitCode == entry2.ExitCode &&
|
||||||
entry1.StartTime.Format(time.RFC3339) == entry2.StartTime.Format(time.RFC3339) &&
|
entry1.StartTime.Format(time.RFC3339) == entry2.StartTime.Format(time.RFC3339) &&
|
||||||
entry1.EndTime.Format(time.RFC3339) == entry2.EndTime.Format(time.RFC3339)
|
entry1.EndTime.Format(time.RFC3339) == entry2.EndTime.Format(time.RFC3339)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MakeFakeHistoryEntry(command string) HistoryEntry {
|
||||||
|
return HistoryEntry{
|
||||||
|
LocalUsername: "david",
|
||||||
|
Hostname: "localhost",
|
||||||
|
Command: command,
|
||||||
|
CurrentWorkingDirectory: "/tmp/",
|
||||||
|
ExitCode: 2,
|
||||||
|
StartTime: time.Now(),
|
||||||
|
EndTime: time.Now(),
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user