Compare commits
No commits in common. "monodevice" and "main" have entirely different histories.
monodevice
...
main
|
@ -1 +0,0 @@
|
||||||
golang 1.19.3
|
|
|
@ -85,7 +85,7 @@ func initialModel(ctx *context.Context, t table.Model, initialQuery string, numE
|
||||||
if initialQuery != "" {
|
if initialQuery != "" {
|
||||||
queryInput.SetValue(initialQuery)
|
queryInput.SetValue(initialQuery)
|
||||||
}
|
}
|
||||||
return model{ctx: ctx, spinner: s, isLoading: false, table: t, runQuery: &initialQuery, queryInput: queryInput, numEntries: numEntries}
|
return model{ctx: ctx, spinner: s, isLoading: true, table: t, runQuery: &initialQuery, queryInput: queryInput, numEntries: numEntries}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m model) Init() tea.Cmd {
|
func (m model) Init() tea.Cmd {
|
||||||
|
@ -199,6 +199,10 @@ func (m model) View() string {
|
||||||
if m.quitting {
|
if m.quitting {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
loadingMessage := ""
|
||||||
|
if m.isLoading {
|
||||||
|
loadingMessage = fmt.Sprintf("%s Loading hishtory entries from other devices...", m.spinner.View())
|
||||||
|
}
|
||||||
warning := ""
|
warning := ""
|
||||||
if m.isOffline {
|
if m.isOffline {
|
||||||
warning += "Warning: failed to contact the hishtory backend (are you offline?), so some results may be stale\n\n"
|
warning += "Warning: failed to contact the hishtory backend (are you offline?), so some results may be stale\n\n"
|
||||||
|
@ -206,7 +210,7 @@ func (m model) View() string {
|
||||||
if m.searchErr != nil {
|
if m.searchErr != nil {
|
||||||
warning += fmt.Sprintf("Warning: failed to search: %v\n\n", m.searchErr)
|
warning += fmt.Sprintf("Warning: failed to search: %v\n\n", m.searchErr)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("\n%s%s\nSearch Query: %s\n\n%s\n", warning, m.banner, m.queryInput.View(), baseStyle.Render(m.table.View()))
|
return fmt.Sprintf("\n%s\n%s%s\nSearch Query: %s\n\n%s\n", loadingMessage, warning, m.banner, m.queryInput.View(), baseStyle.Render(m.table.View()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRows(ctx *context.Context, columnNames []string, query string, numEntries int) ([]table.Row, int, error) {
|
func getRows(ctx *context.Context, columnNames []string, query string, numEntries int) ([]table.Row, int, error) {
|
||||||
|
@ -407,6 +411,32 @@ func TuiQuery(ctx *context.Context, gitCommit, initialQuery string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
p := tea.NewProgram(initialModel(ctx, t, initialQuery, numEntries), tea.WithOutput(os.Stderr))
|
p := tea.NewProgram(initialModel(ctx, t, initialQuery, numEntries), tea.WithOutput(os.Stderr))
|
||||||
|
go func() {
|
||||||
|
err := RetrieveAdditionalEntriesFromRemote(ctx)
|
||||||
|
if err != nil {
|
||||||
|
p.Send(err)
|
||||||
|
}
|
||||||
|
p.Send(doneDownloadingMsg{})
|
||||||
|
}()
|
||||||
|
// Async: Process deletion requests
|
||||||
|
go func() {
|
||||||
|
err := ProcessDeletionRequests(ctx)
|
||||||
|
if err != nil {
|
||||||
|
p.Send(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// Async: Check for any banner from the server
|
||||||
|
go func() {
|
||||||
|
banner, err := GetBanner(ctx, gitCommit)
|
||||||
|
if err != nil {
|
||||||
|
if IsOfflineError(err) {
|
||||||
|
p.Send(offlineMsg{})
|
||||||
|
} else {
|
||||||
|
p.Send(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.Send(bannerMsg{banner: string(banner)})
|
||||||
|
}()
|
||||||
// Blocking: Start the TUI
|
// Blocking: Start the TUI
|
||||||
err = p.Start()
|
err = p.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
95
hishtory.go
95
hishtory.go
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
@ -12,6 +13,7 @@ import (
|
||||||
"github.com/ddworken/hishtory/client/data"
|
"github.com/ddworken/hishtory/client/data"
|
||||||
"github.com/ddworken/hishtory/client/hctx"
|
"github.com/ddworken/hishtory/client/hctx"
|
||||||
"github.com/ddworken/hishtory/client/lib"
|
"github.com/ddworken/hishtory/client/lib"
|
||||||
|
"github.com/ddworken/hishtory/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
var GitCommit string = "Unknown"
|
var GitCommit string = "Unknown"
|
||||||
|
@ -24,6 +26,7 @@ func main() {
|
||||||
switch os.Args[1] {
|
switch os.Args[1] {
|
||||||
case "saveHistoryEntry":
|
case "saveHistoryEntry":
|
||||||
ctx := hctx.MakeContext()
|
ctx := hctx.MakeContext()
|
||||||
|
lib.CheckFatalError(maybeUploadSkippedHistoryEntries(ctx))
|
||||||
saveHistoryEntry(ctx)
|
saveHistoryEntry(ctx)
|
||||||
case "query":
|
case "query":
|
||||||
ctx := hctx.MakeContext()
|
ctx := hctx.MakeContext()
|
||||||
|
@ -295,9 +298,31 @@ Supported commands:
|
||||||
}
|
}
|
||||||
|
|
||||||
func printDumpStatus(config hctx.ClientConfig) {
|
func printDumpStatus(config hctx.ClientConfig) {
|
||||||
|
dumpRequests, err := getDumpRequests(config)
|
||||||
|
lib.CheckFatalError(err)
|
||||||
|
fmt.Printf("Dump Requests: ")
|
||||||
|
for _, d := range dumpRequests {
|
||||||
|
fmt.Printf("%#v, ", *d)
|
||||||
|
}
|
||||||
fmt.Print("\n")
|
fmt.Print("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getDumpRequests(config hctx.ClientConfig) ([]*shared.DumpRequest, error) {
|
||||||
|
if config.IsOffline {
|
||||||
|
return make([]*shared.DumpRequest, 0), nil
|
||||||
|
}
|
||||||
|
resp, err := lib.ApiGet("/api/v1/get-dump-requests?user_id=" + data.UserId(config.UserSecret) + "&device_id=" + config.DeviceId)
|
||||||
|
if lib.IsOfflineError(err) {
|
||||||
|
return []*shared.DumpRequest{}, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var dumpRequests []*shared.DumpRequest
|
||||||
|
err = json.Unmarshal(resp, &dumpRequests)
|
||||||
|
return dumpRequests, err
|
||||||
|
}
|
||||||
|
|
||||||
func query(ctx *context.Context, query string) {
|
func query(ctx *context.Context, query string) {
|
||||||
db := hctx.GetDb(ctx)
|
db := hctx.GetDb(ctx)
|
||||||
err := lib.RetrieveAdditionalEntriesFromRemote(ctx)
|
err := lib.RetrieveAdditionalEntriesFromRemote(ctx)
|
||||||
|
@ -329,6 +354,43 @@ func displayBannerIfSet(ctx *context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func maybeUploadSkippedHistoryEntries(ctx *context.Context) error {
|
||||||
|
config := hctx.GetConf(ctx)
|
||||||
|
if !config.HaveMissedUploads {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if config.IsOffline {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload the missing entries
|
||||||
|
db := hctx.GetDb(ctx)
|
||||||
|
query := fmt.Sprintf("after:%s", time.Unix(config.MissedUploadTimestamp, 0).Format("2006-01-02"))
|
||||||
|
entries, err := lib.Search(ctx, db, query, 0)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to retrieve history entries that haven't been uploaded yet: %v", err)
|
||||||
|
}
|
||||||
|
hctx.GetLogger().Infof("Uploading %d history entries that previously failed to upload (query=%#v)\n", len(entries), query)
|
||||||
|
jsonValue, err := lib.EncryptAndMarshal(config, entries)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = lib.ApiPost("/api/v1/submit?source_device_id="+config.DeviceId, "application/json", jsonValue)
|
||||||
|
if err != nil {
|
||||||
|
// Failed to upload the history entry, so we must still be offline. So just return nil and we'll try again later.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark down that we persisted it
|
||||||
|
config.HaveMissedUploads = false
|
||||||
|
config.MissedUploadTimestamp = 0
|
||||||
|
err = hctx.SetConfig(config)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to mark a history entry as uploaded: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func saveHistoryEntry(ctx *context.Context) {
|
func saveHistoryEntry(ctx *context.Context) {
|
||||||
config := hctx.GetConf(ctx)
|
config := hctx.GetConf(ctx)
|
||||||
if !config.IsEnabled {
|
if !config.IsEnabled {
|
||||||
|
@ -366,6 +428,39 @@ func saveHistoryEntry(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if there is a pending dump request and reply to it if so
|
||||||
|
dumpRequests, err := getDumpRequests(config)
|
||||||
|
if err != nil {
|
||||||
|
if lib.IsOfflineError(err) {
|
||||||
|
// It is fine to just ignore this, the next command will retry the API and eventually we will respond to any pending dump requests
|
||||||
|
dumpRequests = []*shared.DumpRequest{}
|
||||||
|
hctx.GetLogger().Infof("Failed to check for dump requests because we failed to connect to the remote server!")
|
||||||
|
} else {
|
||||||
|
lib.CheckFatalError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(dumpRequests) > 0 {
|
||||||
|
lib.CheckFatalError(lib.RetrieveAdditionalEntriesFromRemote(ctx))
|
||||||
|
entries, err := lib.Search(ctx, db, "", 0)
|
||||||
|
lib.CheckFatalError(err)
|
||||||
|
var encEntries []*shared.EncHistoryEntry
|
||||||
|
for _, entry := range entries {
|
||||||
|
enc, err := data.EncryptHistoryEntry(config.UserSecret, *entry)
|
||||||
|
lib.CheckFatalError(err)
|
||||||
|
encEntries = append(encEntries, &enc)
|
||||||
|
}
|
||||||
|
reqBody, err := json.Marshal(encEntries)
|
||||||
|
lib.CheckFatalError(err)
|
||||||
|
for _, dumpRequest := range dumpRequests {
|
||||||
|
if !config.IsOffline {
|
||||||
|
_, err := lib.ApiPost("/api/v1/submit-dump?user_id="+dumpRequest.UserId+"&requesting_device_id="+dumpRequest.RequestingDeviceId+"&source_device_id="+config.DeviceId, "application/json", reqBody)
|
||||||
|
lib.CheckFatalError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle deletion requests
|
||||||
|
lib.CheckFatalError(lib.ProcessDeletionRequests(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
func export(ctx *context.Context, query string) {
|
func export(ctx *context.Context, query string) {
|
||||||
|
|
Loading…
Reference in New Issue