Wrap up 1.1.0

This commit is contained in:
Mukhtar Akere
2025-08-09 10:55:10 +01:00
parent 7c8156eacf
commit 3aeb806033
54 changed files with 1592 additions and 1523 deletions
+5 -11
View File
@@ -166,7 +166,7 @@ func (m *Manager) performMount(provider, webdavURL string) error {
Args: mountArgs,
}
_, err := m.makeRequest(req)
_, err := m.makeRequest(req, true)
if err != nil {
// Clean up mount point on failure
m.forceUnmountPath(mountPath)
@@ -218,7 +218,7 @@ func (m *Manager) unmount(provider string) error {
var rcErr error
if m.IsReady() {
_, rcErr = m.makeRequest(req)
_, rcErr = m.makeRequest(req, true)
}
// If RC unmount fails or server is not ready, try force unmount
@@ -335,7 +335,7 @@ func (m *Manager) RefreshDir(provider string, dirs []string) error {
Args: args,
}
_, err := m.makeRequest(req)
_, err := m.makeRequest(req, true)
if err != nil {
m.logger.Error().Err(err).
Str("provider", provider).
@@ -348,7 +348,7 @@ func (m *Manager) RefreshDir(provider string, dirs []string) error {
Args: args,
}
_, err = m.makeRequest(req)
_, err = m.makeRequest(req, true)
if err != nil {
m.logger.Error().Err(err).
Str("provider", provider).
@@ -373,16 +373,10 @@ func (m *Manager) createConfig(configName, webdavURL string) error {
},
}
_, err := m.makeRequest(req)
_, err := m.makeRequest(req, true)
if err != nil {
return fmt.Errorf("failed to create config %s: %w", configName, err)
}
m.logger.Trace().
Str("config_name", configName).
Str("webdav_url", webdavURL).
Msg("Rclone config created")
return nil
}
+1 -1
View File
@@ -49,7 +49,7 @@ func (m *Manager) checkMountHealth(provider string) bool {
},
}
_, err := m.makeRequest(req)
_, err := m.makeRequest(req, true)
return err == nil
}
+22 -24
View File
@@ -56,11 +56,6 @@ type RCResponse struct {
Error string `json:"error,omitempty"`
}
type CoreStatsResponse struct {
TransferStats map[string]interface{} `json:"transferStats"`
CoreStats map[string]interface{} `json:"coreStats"`
}
// NewManager creates a new rclone RC manager
func NewManager() *Manager {
cfg := config.Get()
@@ -305,12 +300,11 @@ func (m *Manager) waitForServer() {
// pingServer checks if the RC server is responding
func (m *Manager) pingServer() bool {
req := RCRequest{Command: "core/version"}
_, err := m.makeRequest(req)
_, err := m.makeRequest(req, true)
return err == nil
}
// makeRequest makes a request to the rclone RC server
func (m *Manager) makeRequest(req RCRequest) (*RCResponse, error) {
func (m *Manager) makeRequest(req RCRequest, close bool) (*http.Response, error) {
reqBody, err := json.Marshal(req.Args)
if err != nil {
return nil, fmt.Errorf("failed to marshal request: %w", err)
@@ -328,26 +322,30 @@ func (m *Manager) makeRequest(req RCRequest) (*RCResponse, error) {
if err != nil {
return nil, fmt.Errorf("failed to make request: %w", err)
}
defer func() {
if err := resp.Body.Close(); err != nil {
m.logger.Debug().Err(err).Msg("Failed to close response body")
}
}()
var rcResp RCResponse
if err := json.NewDecoder(resp.Body).Decode(&rcResp); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
if rcResp.Error != "" {
return nil, fmt.Errorf("rclone error: %s", rcResp.Error)
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status code: %d - %s", resp.StatusCode, http.StatusText(resp.StatusCode))
// Read the response body to get more details
defer resp.Body.Close()
var errorResp RCResponse
if err := json.NewDecoder(resp.Body).Decode(&errorResp); err != nil {
return nil, fmt.Errorf("request failed with status %s, but could not decode error response: %w", resp.Status, err)
}
if errorResp.Error != "" {
return nil, fmt.Errorf("%s", errorResp.Error)
} else {
return nil, fmt.Errorf("request failed with status %s and no error message", resp.Status)
}
}
return &rcResp, nil
if close {
defer func() {
if err := resp.Body.Close(); err != nil {
m.logger.Debug().Err(err).Msg("Failed to close response body")
}
}()
}
return resp, nil
}
// IsReady returns true if the RC server is ready
+112 -64
View File
@@ -5,49 +5,118 @@ import (
"fmt"
)
type TransferringStat struct {
Bytes int64 `json:"bytes"`
ETA int64 `json:"eta"`
Name string `json:"name"`
Speed float64 `json:"speed"`
Size int64 `json:"size"`
Progress float64 `json:"progress"`
}
type VersionResponse struct {
Arch string `json:"arch"`
Version string `json:"version"`
OS string `json:"os"`
}
type CoreStatsResponse struct {
Bytes int64 `json:"bytes"`
Checks int `json:"checks"`
DeletedDirs int `json:"deletedDirs"`
Deletes int `json:"deletes"`
ElapsedTime float64 `json:"elapsedTime"`
Errors int `json:"errors"`
Eta int `json:"eta"`
Speed float64 `json:"speed"`
TotalBytes int64 `json:"totalBytes"`
TotalChecks int `json:"totalChecks"`
TotalTransfers int `json:"totalTransfers"`
TransferTime float64 `json:"transferTime"`
Transfers int `json:"transfers"`
Transferring []TransferringStat `json:"transferring,omitempty"`
}
type MemoryStats struct {
Sys int `json:"Sys"`
TotalAlloc int64 `json:"TotalAlloc"`
}
type BandwidthStats struct {
BytesPerSecond int64 `json:"bytesPerSecond"`
Rate string `json:"rate"`
}
// Stats represents rclone statistics
type Stats struct {
CoreStats map[string]interface{} `json:"coreStats"`
TransferStats map[string]interface{} `json:"transferStats"`
MountStats map[string]*MountInfo `json:"mountStats"`
Enabled bool `json:"enabled"`
Ready bool `json:"server_ready"`
Core CoreStatsResponse `json:"core"`
Memory MemoryStats `json:"memory"`
Mount map[string]*MountInfo `json:"mount"`
Bandwidth BandwidthStats `json:"bandwidth"`
Version VersionResponse `json:"version"`
}
// GetStats retrieves statistics from the rclone RC server
func (m *Manager) GetStats() (*Stats, error) {
stats := &Stats{}
stats.Ready = m.IsReady()
stats.Enabled = true
coreStats, err := m.GetCoreStats()
if err == nil {
stats.Core = *coreStats
}
// Get memory usage
memStats, err := m.GetMemoryUsage()
if err == nil {
stats.Memory = *memStats
}
// Get bandwidth stats
bwStats, err := m.GetBandwidthStats()
if err == nil {
stats.Bandwidth = *bwStats
} else {
fmt.Println("Failed to get rclone stats", err)
}
// Get version info
versionResp, err := m.GetVersion()
if err == nil {
stats.Version = *versionResp
}
// Get mount info
stats.Mount = m.GetAllMounts()
return stats, nil
}
func (m *Manager) GetCoreStats() (*CoreStatsResponse, error) {
if !m.IsReady() {
return nil, fmt.Errorf("rclone RC server not ready")
}
// Get core stats
req := RCRequest{
Command: "core/stats",
}
resp, err := m.makeRequest(req)
resp, err := m.makeRequest(req, false)
if err != nil {
return nil, fmt.Errorf("failed to get rclone stats: %w", err)
return nil, fmt.Errorf("failed to get core stats: %w", err)
}
defer resp.Body.Close()
// Parse the response
var coreStatsResp CoreStatsResponse
if respBytes, err := json.Marshal(resp.Result); err == nil {
json.Unmarshal(respBytes, &coreStatsResp)
var coreStats CoreStatsResponse
if err := json.NewDecoder(resp.Body).Decode(&coreStats); err != nil {
return nil, fmt.Errorf("failed to decode core stats response: %w", err)
}
// Get mount stats
mountStats := m.GetAllMounts()
stats := &Stats{
CoreStats: coreStatsResp.CoreStats,
TransferStats: coreStatsResp.TransferStats,
MountStats: mountStats,
}
return stats, nil
return &coreStats, nil
}
// GetMemoryUsage returns memory usage statistics
func (m *Manager) GetMemoryUsage() (map[string]interface{}, error) {
func (m *Manager) GetMemoryUsage() (*MemoryStats, error) {
if !m.IsReady() {
return nil, fmt.Errorf("rclone RC server not ready")
}
@@ -56,20 +125,21 @@ func (m *Manager) GetMemoryUsage() (map[string]interface{}, error) {
Command: "core/memstats",
}
resp, err := m.makeRequest(req)
resp, err := m.makeRequest(req, false)
if err != nil {
return nil, fmt.Errorf("failed to get memory stats: %w", err)
}
defer resp.Body.Close()
var memStats MemoryStats
if memStats, ok := resp.Result.(map[string]interface{}); ok {
return memStats, nil
if err := json.NewDecoder(resp.Body).Decode(&memStats); err != nil {
return nil, fmt.Errorf("failed to decode memory stats response: %w", err)
}
return nil, fmt.Errorf("invalid memory stats response")
return &memStats, nil
}
// GetBandwidthStats returns bandwidth usage for all transfers
func (m *Manager) GetBandwidthStats() (map[string]interface{}, error) {
func (m *Manager) GetBandwidthStats() (*BandwidthStats, error) {
if !m.IsReady() {
return nil, fmt.Errorf("rclone RC server not ready")
}
@@ -78,21 +148,21 @@ func (m *Manager) GetBandwidthStats() (map[string]interface{}, error) {
Command: "core/bwlimit",
}
resp, err := m.makeRequest(req)
resp, err := m.makeRequest(req, false)
if err != nil {
// Bandwidth stats might not be available, return empty
return map[string]interface{}{}, nil
return nil, nil
}
if bwStats, ok := resp.Result.(map[string]interface{}); ok {
return bwStats, nil
defer resp.Body.Close()
var bwStats BandwidthStats
if err := json.NewDecoder(resp.Body).Decode(&bwStats); err != nil {
return nil, fmt.Errorf("failed to decode bandwidth stats response: %w", err)
}
return map[string]interface{}{}, nil
return &bwStats, nil
}
// GetVersion returns rclone version information
func (m *Manager) GetVersion() (map[string]interface{}, error) {
func (m *Manager) GetVersion() (*VersionResponse, error) {
if !m.IsReady() {
return nil, fmt.Errorf("rclone RC server not ready")
}
@@ -101,36 +171,14 @@ func (m *Manager) GetVersion() (map[string]interface{}, error) {
Command: "core/version",
}
resp, err := m.makeRequest(req)
resp, err := m.makeRequest(req, false)
if err != nil {
return nil, fmt.Errorf("failed to get version: %w", err)
}
if version, ok := resp.Result.(map[string]interface{}); ok {
return version, nil
defer resp.Body.Close()
var versionResp VersionResponse
if err := json.NewDecoder(resp.Body).Decode(&versionResp); err != nil {
return nil, fmt.Errorf("failed to decode version response: %w", err)
}
return nil, fmt.Errorf("invalid version response")
}
// GetConfigDump returns the current rclone configuration
func (m *Manager) GetConfigDump() (map[string]interface{}, error) {
if !m.IsReady() {
return nil, fmt.Errorf("rclone RC server not ready")
}
req := RCRequest{
Command: "config/dump",
}
resp, err := m.makeRequest(req)
if err != nil {
return nil, fmt.Errorf("failed to get config dump: %w", err)
}
if config, ok := resp.Result.(map[string]interface{}); ok {
return config, nil
}
return nil, fmt.Errorf("invalid config dump response")
return &versionResp, nil
}