Handle more server-side errors from the client

This commit is contained in:
2025-12-10 00:08:42 -05:00
parent e355c810da
commit f16078fe01
5 changed files with 49 additions and 16 deletions
+9 -2
View File
@@ -2,9 +2,9 @@ package main
import ( import (
"bufio" "bufio"
"encoding/json"
"fmt" "fmt"
"os" "os"
"strconv"
"strings" "strings"
"github.com/rwinkhart/go-boilerplate/back" "github.com/rwinkhart/go-boilerplate/back"
@@ -110,8 +110,15 @@ func main() {
} }
} }
} }
// print EntryRoot, AgeDir and bool indicating OS type to stdout for client to store in config // print EntryRoot, AgeDir and bool indicating OS type to stdout for client to store in config
fmt.Print(global.EntryRoot + global.FSSpace + global.AgeDir + global.FSSpace + strconv.FormatBool(global.IsWindows)) registerResp := synccommon.RegisterResp{EntryRoot: global.EntryRoot, AgeDir: global.AgeDir, IsWindows: global.IsWindows}
registerRespBytes, err := json.Marshal(registerResp)
if err != nil {
fmt.Printf("{\"errMsg\":\"%s\"}", err.Error())
return
}
fmt.Print(string(registerRespBytes))
case "init": case "init":
// create the necessary directories for libmuttonserver to function // create the necessary directories for libmuttonserver to function
_, err := global.DirInit(false) _, err := global.DirInit(false)
+2 -1
View File
@@ -561,5 +561,6 @@ func RunJob(returnLists bool) ([3][]string, error) {
if err != nil { if err != nil {
return [3][]string{nil, nil, nil}, errors.New("unable to sync entries: " + err.Error()) return [3][]string{nil, nil, nil}, errors.New("unable to sync entries: " + err.Error())
} }
return lists, nil // dummy return for when not returning lists _ = sshClient.Close() // ignore error; non-critical/unlikely/not much could be done about it
return lists, nil // dummy return for when not returning lists
} }
+15 -3
View File
@@ -22,6 +22,7 @@ func ShearRemoteFromClient(vanityPath string, onlyShearAgeFile bool) error {
} }
var modifier string var modifier string
var output []byte
sshClient, offlineMode, _, _, _, err := GetSSHClient() sshClient, offlineMode, _, _, _, err := GetSSHClient()
if offlineMode { if offlineMode {
goto end goto end
@@ -42,10 +43,13 @@ func ShearRemoteFromClient(vanityPath string, onlyShearAgeFile bool) error {
if onlyShearAgeFile { if onlyShearAgeFile {
modifier = "-age" modifier = "-age"
} }
_, err = GetSSHOutput(sshClient, "libmuttonserver shear"+modifier, deviceID+"\n"+strings.ReplaceAll(vanityPath, global.PathSeparator, global.FSPath)) output, err = GetSSHOutput(sshClient, "libmuttonserver shear"+modifier, deviceID+"\n"+strings.ReplaceAll(vanityPath, global.PathSeparator, global.FSPath))
if err != nil { if err != nil {
return errors.New("unable to shear target remotely: " + err.Error()) return errors.New("unable to shear target remotely: " + err.Error())
} }
if len(output) > 11 {
return errors.New("unable to complete shear; server-side error occurred: " + string(output)[11:len(output)-2])
}
// close the SSH client // close the SSH client
err = sshClient.Close() err = sshClient.Close()
@@ -78,6 +82,7 @@ func RenameRemoteFromClient(oldVanityPath, newVanityPath string) error {
} }
// create an SSH client // create an SSH client
var output []byte
sshClient, offlineMode, _, _, _, err := GetSSHClient() sshClient, offlineMode, _, _, _, err := GetSSHClient()
if offlineMode { if offlineMode {
goto end goto end
@@ -87,13 +92,16 @@ func RenameRemoteFromClient(oldVanityPath, newVanityPath string) error {
} }
// call the server to move the target on the remote system and add the old target to the deletions list // call the server to move the target on the remote system and add the old target to the deletions list
_, err = GetSSHOutput(sshClient, "libmuttonserver rename", output, err = GetSSHOutput(sshClient, "libmuttonserver rename",
(deviceIDList)[0].Name()+"\n"+ (deviceIDList)[0].Name()+"\n"+
strings.ReplaceAll(oldVanityPath, global.PathSeparator, global.FSPath)+"\n"+ strings.ReplaceAll(oldVanityPath, global.PathSeparator, global.FSPath)+"\n"+
strings.ReplaceAll(newVanityPath, global.PathSeparator, global.FSPath)) strings.ReplaceAll(newVanityPath, global.PathSeparator, global.FSPath))
if err != nil { if err != nil {
return errors.New("unable to rename target remotely: " + err.Error()) return errors.New("unable to rename target remotely: " + err.Error())
} }
if len(output) > 11 {
return errors.New("unable to complete rename; server-side error occurred: " + string(output)[11:len(output)-2])
}
// close the SSH client // close the SSH client
err = sshClient.Close() err = sshClient.Close()
@@ -118,6 +126,7 @@ func AddFolderRemoteFromClient(vanityPath string) error {
} }
// create an SSH client // create an SSH client
var output []byte
sshClient, offlineMode, _, _, _, err := GetSSHClient() sshClient, offlineMode, _, _, _, err := GetSSHClient()
if offlineMode { if offlineMode {
goto end goto end
@@ -127,10 +136,13 @@ func AddFolderRemoteFromClient(vanityPath string) error {
} }
// call the server to create the folder remotely // call the server to create the folder remotely
_, err = GetSSHOutput(sshClient, "libmuttonserver addfolder", strings.ReplaceAll(vanityPath, global.PathSeparator, global.FSPath)) // call the server to create the folder remotely output, err = GetSSHOutput(sshClient, "libmuttonserver addfolder", strings.ReplaceAll(vanityPath, global.PathSeparator, global.FSPath)) // call the server to create the folder remotely
if err != nil { if err != nil {
return errors.New("unable to add folder remotely: " + err.Error()) return errors.New("unable to add folder remotely: " + err.Error())
} }
if len(output) > 11 {
return errors.New("unable to complete addfolder; server-side error occurred: " + string(output)[11:len(output)-2])
}
// close the SSH client // close the SSH client
err = sshClient.Close() err = sshClient.Close()
+11 -7
View File
@@ -16,25 +16,31 @@ const (
AnsiDelete = "\033[38;5;1m" AnsiDelete = "\033[38;5;1m"
) )
// FetchResponse defines the structure of responses from `libmuttonserver fetch`. // FetchResp defines the structure of responses from `libmuttonserver fetch`.
type FetchResp struct { type FetchResp struct {
ErrMsg *string `json:"errMsg"` // nil if no error occurred ErrMsg *string `json:"errMsg"` // nil if no error occurred
ServerTime int64 `json:"serverTime"` ServerTime int64 `json:"serverTime"`
Deletions []Deletion `json:"deletions"` Deletions []Deletion `json:"deletions"`
FoldersToEntries map[string][]Entry `json:"folders"` FoldersToEntries map[string][]Entry `json:"folders"`
} }
type Deletion struct { type Deletion struct {
VanityPath string `json:"vanityPath"` VanityPath string `json:"vanityPath"`
IsAgeFile bool `json:"isAgeFile"` IsAgeFile bool `json:"isAgeFile"`
} }
type Entry struct { type Entry struct {
VanityPath string `json:"vanityPath"` VanityPath string `json:"vanityPath"`
ModTime int64 `json:"modTime"` ModTime int64 `json:"modTime"`
AgeTimestamp *int64 `json:"ageTimestamp"` // nil if no age file is present (non-password entry) AgeTimestamp *int64 `json:"ageTimestamp"` // nil if no age file is present (non-password entry)
} }
// RegisterResp defines the structure of responses from `libmuttonserver register`
type RegisterResp struct {
ErrMsg *string `json:"errMsg"` // nil if no error occurred
EntryRoot string `json:"entryRoot"`
AgeDir string `json:"ageDir"`
IsWindows bool `json:"isWindows"`
}
// GetModTimes returns a list of all entry modification times. // GetModTimes returns a list of all entry modification times.
func GetModTimes(entryList []string) []int64 { func GetModTimes(entryList []string) []int64 {
var modList []int64 var modList []int64
@@ -70,17 +76,15 @@ func ShearLocal(vanityPath, clientDeviceID string, onlyShearAgeFile bool) (strin
if !onlyShearAgeFile { if !onlyShearAgeFile {
f, err := os.OpenFile(global.ConfigDir+global.PathSeparator+"deletions"+global.PathSeparator+device.Name()+global.FSSpace+"entry"+global.FSSpace+strings.ReplaceAll(vanityPath, "/", global.FSPath), os.O_CREATE|os.O_WRONLY, 0600) f, err := os.OpenFile(global.ConfigDir+global.PathSeparator+"deletions"+global.PathSeparator+device.Name()+global.FSSpace+"entry"+global.FSSpace+strings.ReplaceAll(vanityPath, "/", global.FSPath), os.O_CREATE|os.O_WRONLY, 0600)
if err != nil { if err != nil {
// do not print error as there is currently no way of seeing server-side errors
// failure to add the target to the deletions list will exit the program and result in a client re-uploading the target (non-critical) // failure to add the target to the deletions list will exit the program and result in a client re-uploading the target (non-critical)
os.Exit(back.ErrorWrite) return "", false, err
} }
_ = f.Close() // error ignored; if the file could be created, it can probably be closed _ = f.Close() // error ignored; if the file could be created, it can probably be closed
} }
f, err := os.OpenFile(global.ConfigDir+global.PathSeparator+"deletions"+global.PathSeparator+device.Name()+global.FSSpace+"age"+global.FSSpace+strings.ReplaceAll(vanityPath, "/", global.FSPath), os.O_CREATE|os.O_WRONLY, 0600) f, err := os.OpenFile(global.ConfigDir+global.PathSeparator+"deletions"+global.PathSeparator+device.Name()+global.FSSpace+"age"+global.FSSpace+strings.ReplaceAll(vanityPath, "/", global.FSPath), os.O_CREATE|os.O_WRONLY, 0600)
if err != nil { if err != nil {
// do not print error as there is currently no way of seeing server-side errors
// failure to add the target to the deletions list will exit the program and result in a client re-uploading the target (non-critical) // failure to add the target to the deletions list will exit the program and result in a client re-uploading the target (non-critical)
os.Exit(back.ErrorWrite) return "", false, err
} }
_ = f.Close() // error ignored; if the file could be created, it can probably be closed _ = f.Close() // error ignored; if the file could be created, it can probably be closed
} }
+12 -3
View File
@@ -1,16 +1,17 @@
package synccycles package synccycles
import ( import (
"encoding/json"
"errors" "errors"
"math/rand" "math/rand"
"os" "os"
"strconv" "strconv"
"strings"
"time" "time"
"github.com/rwinkhart/go-boilerplate/back" "github.com/rwinkhart/go-boilerplate/back"
"github.com/rwinkhart/libmutton/global" "github.com/rwinkhart/libmutton/global"
"github.com/rwinkhart/libmutton/syncclient" "github.com/rwinkhart/libmutton/syncclient"
"github.com/rwinkhart/libmutton/synccommon"
) )
// DeviceIDGen generates a new client device ID and registers it with the server (will replace existing one). // DeviceIDGen generates a new client device ID and registers it with the server (will replace existing one).
@@ -59,7 +60,15 @@ func DeviceIDGen(oldDeviceID, prefix string) (string, string, string, error) {
cleanupOnFail() cleanupOnFail()
return "", "", "", errors.New("unable to register device ID with server: " + err.Error()) return "", "", "", errors.New("unable to register device ID with server: " + err.Error())
} }
sshEntryRootSSHAgeDirSSHIsWindows := strings.Split(string(output), global.FSSpace) var registerResp synccommon.RegisterResp
err = json.Unmarshal(output, &registerResp)
if err != nil {
cleanupOnFail()
return "", "", "", errors.New("unable to unmarshal server register response: " + err.Error())
}
if registerResp.ErrMsg != nil {
return "", "", "", errors.New("unable to complete register; server-side error occurred: " + *registerResp.ErrMsg)
}
_ = sshClient.Close() // ignore error; non-critical/unlikely/not much could be done about it _ = sshClient.Close() // ignore error; non-critical/unlikely/not much could be done about it
// remove old device ID file (locally; may not exist) // remove old device ID file (locally; may not exist)
@@ -68,5 +77,5 @@ func DeviceIDGen(oldDeviceID, prefix string) (string, string, string, error) {
return "", "", "", errors.New("unable to remove old device ID file (locally): " + err.Error()) return "", "", "", errors.New("unable to remove old device ID file (locally): " + err.Error())
} }
return sshEntryRootSSHAgeDirSSHIsWindows[0], sshEntryRootSSHAgeDirSSHIsWindows[1], sshEntryRootSSHAgeDirSSHIsWindows[2], nil return registerResp.EntryRoot, registerResp.AgeDir, strconv.FormatBool(registerResp.IsWindows), nil
} }