leader discovery fix and code cleaning
This commit is contained in:
@@ -9,12 +9,16 @@ import (
|
||||
|
||||
// Handlers provides HTTP handlers for mount operations
|
||||
type Handlers struct {
|
||||
manager *Manager
|
||||
manager *Manager
|
||||
scanFunc ScanFunc
|
||||
}
|
||||
|
||||
// NewHandlers creates new mount handlers
|
||||
func NewHandlers(manager *Manager) *Handlers {
|
||||
return &Handlers{manager: manager}
|
||||
func NewHandlers(manager *Manager, scanFunc ScanFunc) *Handlers {
|
||||
return &Handlers{
|
||||
manager: manager,
|
||||
scanFunc: scanFunc,
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterRoutes registers all mount-related routes
|
||||
@@ -24,10 +28,12 @@ func (h *Handlers) RegisterRoutes(r *mux.Router) {
|
||||
r.HandleFunc("/api/mounts", h.handleCreateMount).Methods("POST")
|
||||
r.HandleFunc("/api/mounts/rclone/remotes", h.handleListRcloneRemotes).Methods("GET")
|
||||
r.HandleFunc("/api/mounts/{id}", h.handleGetMount).Methods("GET")
|
||||
r.HandleFunc("/api/mounts/{id}", h.handleUpdateMount).Methods("PUT", "PATCH")
|
||||
r.HandleFunc("/api/mounts/{id}", h.handleDeleteMount).Methods("DELETE")
|
||||
r.HandleFunc("/api/mounts/{id}/mount", h.handleRequestMount).Methods("POST")
|
||||
r.HandleFunc("/api/mounts/{id}/unmount", h.handleRequestUnmount).Methods("POST")
|
||||
r.HandleFunc("/api/mounts/{id}/safe-unmount", h.handleSafeUnmount).Methods("POST")
|
||||
r.HandleFunc("/api/mounts/{id}/scan", h.handleTriggerMountScan).Methods("POST")
|
||||
}
|
||||
|
||||
// handleListMounts handles GET /api/mounts
|
||||
@@ -194,6 +200,90 @@ func (h *Handlers) handleListRcloneRemotes(w http.ResponseWriter, r *http.Reques
|
||||
writeJSON(w, http.StatusOK, RcloneRemotesResponse{Remotes: remotes})
|
||||
}
|
||||
|
||||
// handleUpdateMount handles PUT/PATCH /api/mounts/{id}
|
||||
func (h *Handlers) handleUpdateMount(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
var updates map[string]interface{}
|
||||
if err := json.NewDecoder(r.Body).Decode(&updates); err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid JSON body")
|
||||
return
|
||||
}
|
||||
|
||||
mount, err := h.manager.UpdateMount(id, updates)
|
||||
if err != nil {
|
||||
if err.Error() == "mount not found" {
|
||||
writeError(w, http.StatusNotFound, err.Error())
|
||||
return
|
||||
}
|
||||
writeError(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, MountResponse{Mount: mount})
|
||||
}
|
||||
|
||||
// handleTriggerMountScan handles POST /api/mounts/{id}/scan
|
||||
func (h *Handlers) handleTriggerMountScan(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
// Get mount info
|
||||
mount, err := h.manager.GetMount(id)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if mount == nil {
|
||||
writeError(w, http.StatusNotFound, "mount not found")
|
||||
return
|
||||
}
|
||||
|
||||
// Check if mount is mounted
|
||||
if !mount.Mounted {
|
||||
writeJSON(w, http.StatusBadRequest, MountScanResponse{
|
||||
Status: "error",
|
||||
Message: "mount is not currently mounted",
|
||||
MountID: mount.ID,
|
||||
MountPath: mount.MountPath,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Check if scan function is available
|
||||
if h.scanFunc == nil {
|
||||
writeJSON(w, http.StatusServiceUnavailable, MountScanResponse{
|
||||
Status: "error",
|
||||
Message: "file watcher not available",
|
||||
MountID: mount.ID,
|
||||
MountPath: mount.MountPath,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Trigger scan
|
||||
fileCount, err := h.scanFunc(mount.MountPath)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, MountScanResponse{
|
||||
Status: "error",
|
||||
Message: err.Error(),
|
||||
MountID: mount.ID,
|
||||
MountPath: mount.MountPath,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, MountScanResponse{
|
||||
Status: "ok",
|
||||
Message: "scan triggered successfully",
|
||||
MountID: mount.ID,
|
||||
MountPath: mount.MountPath,
|
||||
FileCount: fileCount,
|
||||
})
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
|
||||
func writeJSON(w http.ResponseWriter, status int, data interface{}) {
|
||||
|
||||
+150
-13
@@ -23,6 +23,7 @@ type Manager struct {
|
||||
mu sync.RWMutex
|
||||
filesPath string
|
||||
mountsDir string
|
||||
poller *Poller
|
||||
}
|
||||
|
||||
// NewManager creates a new mount manager
|
||||
@@ -41,6 +42,11 @@ func NewManager(cfg *config.Config) (*Manager, error) {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// SetPoller sets the poller for the manager
|
||||
func (m *Manager) SetPoller(poller *Poller) {
|
||||
m.poller = poller
|
||||
}
|
||||
|
||||
// ensureDirs creates required directories
|
||||
func (m *Manager) ensureDirs() error {
|
||||
dirs := []string{
|
||||
@@ -95,14 +101,30 @@ func (m *Manager) writeConfig(mountsFile *MountsFile) error {
|
||||
return os.WriteFile(m.config.MountsFilePath(), data, 0644)
|
||||
}
|
||||
|
||||
// IsMounted checks if a path is currently mounted
|
||||
// IsMounted checks if a path is currently mounted by parsing /proc/mounts
|
||||
// Note: Uses /proc/mounts instead of findmnt for Alpine compatibility
|
||||
func (m *Manager) IsMounted(mountPath string) bool {
|
||||
cmd := exec.Command("findmnt", "-n", mountPath)
|
||||
output, err := cmd.CombinedOutput()
|
||||
data, err := os.ReadFile("/proc/mounts")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return strings.TrimSpace(string(output)) != ""
|
||||
|
||||
// Clean the mount path (remove trailing slash)
|
||||
cleanPath := strings.TrimSuffix(mountPath, "/")
|
||||
|
||||
// Parse each line of /proc/mounts
|
||||
lines := strings.Split(string(data), "\n")
|
||||
for _, line := range lines {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) >= 2 {
|
||||
// Field 1 is the mount point
|
||||
mountPoint := strings.TrimSuffix(fields[1], "/")
|
||||
if mountPoint == cleanPath {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ReadError reads the error file for a mount
|
||||
@@ -164,6 +186,26 @@ func ObscurePassword(password string) (string, error) {
|
||||
|
||||
// ListMounts returns all mounts with their current status
|
||||
func (m *Manager) ListMounts() ([]MountStatus, error) {
|
||||
statuses, err := m.listMountsWithoutPollingStatus()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Add polling status from poller (done separately to avoid deadlock)
|
||||
if m.poller != nil {
|
||||
for i := range statuses {
|
||||
pollingStatus := m.poller.GetPollingStatus(statuses[i].ID)
|
||||
statuses[i].PollingActive = pollingStatus.Active
|
||||
statuses[i].LastPolledScan = pollingStatus.LastScan
|
||||
}
|
||||
}
|
||||
|
||||
return statuses, nil
|
||||
}
|
||||
|
||||
// listMountsWithoutPollingStatus returns mounts without querying the poller
|
||||
// This is used internally by the poller to avoid deadlocks
|
||||
func (m *Manager) listMountsWithoutPollingStatus() ([]MountStatus, error) {
|
||||
mountsFile, err := m.readConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -197,12 +239,21 @@ func (m *Manager) GetMount(id string) (*MountStatus, error) {
|
||||
mounted := m.IsMounted(mount.MountPath)
|
||||
errMsg, _ := m.ReadError(mount.ID)
|
||||
|
||||
return &MountStatus{
|
||||
status := &MountStatus{
|
||||
MountConfig: mount,
|
||||
Mounted: mounted,
|
||||
Error: errMsg,
|
||||
LastChecked: NowMS(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Add polling status from poller (safe since GetMount isn't called from poller with lock held)
|
||||
if m.poller != nil {
|
||||
pollingStatus := m.poller.GetPollingStatus(mount.ID)
|
||||
status.PollingActive = pollingStatus.Active
|
||||
status.LastPolledScan = pollingStatus.LastScan
|
||||
}
|
||||
|
||||
return status, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,14 +309,26 @@ func (m *Manager) CreateMount(req *CreateMountRequest) (*MountStatus, error) {
|
||||
enabled = *req.Enabled
|
||||
}
|
||||
|
||||
// Default polling settings
|
||||
pollingEnabled := false
|
||||
if req.PollingEnabled != nil {
|
||||
pollingEnabled = *req.PollingEnabled
|
||||
}
|
||||
pollingIntervalMs := DefaultPollingIntervalMs
|
||||
if req.PollingIntervalMs != nil && *req.PollingIntervalMs >= MinPollingIntervalMs {
|
||||
pollingIntervalMs = *req.PollingIntervalMs
|
||||
}
|
||||
|
||||
mount := MountConfig{
|
||||
ID: id,
|
||||
Name: req.Name,
|
||||
Type: req.Type,
|
||||
Enabled: enabled,
|
||||
DesiredMounted: enabled, // Auto-mount if enabled
|
||||
MountPath: mountPath,
|
||||
Options: req.Options,
|
||||
ID: id,
|
||||
Name: req.Name,
|
||||
Type: req.Type,
|
||||
Enabled: enabled,
|
||||
DesiredMounted: enabled, // Auto-mount if enabled
|
||||
MountPath: mountPath,
|
||||
Options: req.Options,
|
||||
PollingEnabled: pollingEnabled,
|
||||
PollingIntervalMs: pollingIntervalMs,
|
||||
}
|
||||
|
||||
// Type-specific fields
|
||||
@@ -297,6 +360,11 @@ func (m *Manager) CreateMount(req *CreateMountRequest) (*MountStatus, error) {
|
||||
|
||||
log.Printf("[Mounts] Created mount config: %s (%s) -> %s", mount.Name, mount.Type, mount.MountPath)
|
||||
|
||||
// Notify poller about new mount
|
||||
if m.poller != nil {
|
||||
m.poller.NotifyMountChanged()
|
||||
}
|
||||
|
||||
return &MountStatus{
|
||||
MountConfig: mount,
|
||||
Mounted: false,
|
||||
@@ -304,6 +372,63 @@ func (m *Manager) CreateMount(req *CreateMountRequest) (*MountStatus, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UpdateMount updates mount configuration (for polling settings)
|
||||
func (m *Manager) UpdateMount(id string, updates map[string]interface{}) (*MountStatus, error) {
|
||||
mountsFile, err := m.readConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
index := -1
|
||||
for i, mount := range mountsFile.Mounts {
|
||||
if mount.ID == id {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if index == -1 {
|
||||
return nil, fmt.Errorf("mount not found")
|
||||
}
|
||||
|
||||
// Apply updates
|
||||
mount := &mountsFile.Mounts[index]
|
||||
|
||||
if val, ok := updates["pollingEnabled"]; ok {
|
||||
if enabled, ok := val.(bool); ok {
|
||||
mount.PollingEnabled = enabled
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := updates["pollingIntervalMs"]; ok {
|
||||
// Handle both int and float64 (JSON numbers are float64)
|
||||
switch v := val.(type) {
|
||||
case int:
|
||||
if v >= MinPollingIntervalMs {
|
||||
mount.PollingIntervalMs = v
|
||||
}
|
||||
case float64:
|
||||
if int(v) >= MinPollingIntervalMs {
|
||||
mount.PollingIntervalMs = int(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := m.writeConfig(mountsFile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("[Mounts] Updated mount config: %s", mount.Name)
|
||||
|
||||
// Notify poller about config change
|
||||
if m.poller != nil {
|
||||
m.poller.NotifyMountChanged()
|
||||
}
|
||||
|
||||
// Return updated status
|
||||
return m.GetMount(id)
|
||||
}
|
||||
|
||||
// RequestMount sets desiredMounted to true
|
||||
func (m *Manager) RequestMount(id string) error {
|
||||
mountsFile, err := m.readConfig()
|
||||
@@ -318,6 +443,12 @@ func (m *Manager) RequestMount(id string) error {
|
||||
return err
|
||||
}
|
||||
log.Printf("[Mounts] Mount requested: %s", mount.Name)
|
||||
|
||||
// Notify poller about state change
|
||||
if m.poller != nil {
|
||||
m.poller.NotifyMountChanged()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -339,6 +470,12 @@ func (m *Manager) RequestUnmount(id string) error {
|
||||
return err
|
||||
}
|
||||
log.Printf("[Mounts] Unmount requested: %s", mount.Name)
|
||||
|
||||
// Notify poller about state change
|
||||
if m.poller != nil {
|
||||
m.poller.NotifyMountChanged()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
package mounts
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ScanFunc is a function type for scanning a mount path
|
||||
type ScanFunc func(path string) (int, error)
|
||||
|
||||
// mountPoller manages polling for a single mount
|
||||
type mountPoller struct {
|
||||
mountID string
|
||||
stopChan chan struct{}
|
||||
stopped bool
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// PollingStatus holds the polling state for a mount
|
||||
type PollingStatus struct {
|
||||
Active bool `json:"active"`
|
||||
LastScan int64 `json:"lastScan,omitempty"`
|
||||
}
|
||||
|
||||
// Poller manages per-mount polling goroutines
|
||||
type Poller struct {
|
||||
manager *Manager
|
||||
scanFunc ScanFunc
|
||||
|
||||
pollers map[string]*mountPoller // key: mount ID
|
||||
status map[string]*PollingStatus
|
||||
|
||||
running bool
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewPoller creates a new mount poller
|
||||
func NewPoller(manager *Manager, scanFunc ScanFunc) *Poller {
|
||||
return &Poller{
|
||||
manager: manager,
|
||||
scanFunc: scanFunc,
|
||||
pollers: make(map[string]*mountPoller),
|
||||
status: make(map[string]*PollingStatus),
|
||||
}
|
||||
}
|
||||
|
||||
// Start begins the polling service
|
||||
func (p *Poller) Start() error {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
if p.running {
|
||||
return nil
|
||||
}
|
||||
p.running = true
|
||||
|
||||
log.Println("[Poller] Starting mount polling service")
|
||||
|
||||
// Initial sync with mount configurations
|
||||
go p.syncWithMounts()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop stops all polling goroutines
|
||||
func (p *Poller) Stop() error {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
if !p.running {
|
||||
return nil
|
||||
}
|
||||
p.running = false
|
||||
|
||||
log.Println("[Poller] Stopping mount polling service")
|
||||
|
||||
// Stop all pollers
|
||||
for id, poller := range p.pollers {
|
||||
p.stopPoller(poller)
|
||||
delete(p.pollers, id)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SyncWithMounts synchronizes pollers with current mount configurations
|
||||
func (p *Poller) SyncWithMounts() {
|
||||
p.syncWithMounts()
|
||||
}
|
||||
|
||||
// syncWithMounts is the internal implementation
|
||||
func (p *Poller) syncWithMounts() {
|
||||
p.mu.RLock()
|
||||
running := p.running
|
||||
p.mu.RUnlock()
|
||||
|
||||
if !running {
|
||||
return
|
||||
}
|
||||
|
||||
// Get current mounts BEFORE acquiring the write lock to avoid deadlock
|
||||
// (ListMounts calls GetPollingStatus which needs RLock)
|
||||
mounts, err := p.manager.listMountsWithoutPollingStatus()
|
||||
if err != nil {
|
||||
log.Printf("[Poller] Failed to list mounts: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
// Re-check running state after acquiring lock
|
||||
if !p.running {
|
||||
return
|
||||
}
|
||||
|
||||
// Track which mounts we've seen
|
||||
seenMounts := make(map[string]bool)
|
||||
|
||||
for _, mount := range mounts {
|
||||
seenMounts[mount.ID] = true
|
||||
|
||||
// Check if mount should be polled
|
||||
shouldPoll := mount.PollingEnabled && mount.Mounted
|
||||
|
||||
existingPoller, exists := p.pollers[mount.ID]
|
||||
|
||||
if shouldPoll {
|
||||
// Calculate interval
|
||||
interval := mount.PollingIntervalMs
|
||||
if interval < MinPollingIntervalMs {
|
||||
interval = DefaultPollingIntervalMs
|
||||
}
|
||||
|
||||
if !exists {
|
||||
// Start new poller
|
||||
log.Printf("[Poller] Starting poller for mount %s (interval: %dms)", mount.Name, interval)
|
||||
p.startPollerForMount(mount.ID, mount.MountPath, interval)
|
||||
}
|
||||
} else if exists {
|
||||
// Stop existing poller
|
||||
log.Printf("[Poller] Stopping poller for mount %s", mount.Name)
|
||||
p.stopPoller(existingPoller)
|
||||
delete(p.pollers, mount.ID)
|
||||
}
|
||||
}
|
||||
|
||||
// Stop pollers for mounts that no longer exist
|
||||
for id, poller := range p.pollers {
|
||||
if !seenMounts[id] {
|
||||
log.Printf("[Poller] Stopping poller for removed mount %s", id)
|
||||
p.stopPoller(poller)
|
||||
delete(p.pollers, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// startPollerForMount starts a polling goroutine for a mount
|
||||
func (p *Poller) startPollerForMount(mountID, mountPath string, intervalMs int) {
|
||||
poller := &mountPoller{
|
||||
mountID: mountID,
|
||||
stopChan: make(chan struct{}),
|
||||
}
|
||||
p.pollers[mountID] = poller
|
||||
|
||||
// Initialize status
|
||||
if _, exists := p.status[mountID]; !exists {
|
||||
p.status[mountID] = &PollingStatus{}
|
||||
}
|
||||
p.status[mountID].Active = true
|
||||
|
||||
go func() {
|
||||
ticker := time.NewTicker(time.Duration(intervalMs) * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-poller.stopChan:
|
||||
return
|
||||
case <-ticker.C:
|
||||
p.runPollScan(mountID, mountPath)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// stopPoller stops a mount poller
|
||||
func (p *Poller) stopPoller(poller *mountPoller) {
|
||||
poller.mu.Lock()
|
||||
defer poller.mu.Unlock()
|
||||
|
||||
if poller.stopped {
|
||||
return
|
||||
}
|
||||
poller.stopped = true
|
||||
close(poller.stopChan)
|
||||
|
||||
// Update status
|
||||
if status, exists := p.status[poller.mountID]; exists {
|
||||
status.Active = false
|
||||
}
|
||||
}
|
||||
|
||||
// runPollScan executes a scan for a mount
|
||||
func (p *Poller) runPollScan(mountID, mountPath string) {
|
||||
if p.scanFunc == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fileCount, err := p.scanFunc(mountPath)
|
||||
if err != nil {
|
||||
log.Printf("[Poller] Scan failed for mount %s: %v", mountID, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Update status
|
||||
p.mu.Lock()
|
||||
if status, exists := p.status[mountID]; exists {
|
||||
status.LastScan = NowMS()
|
||||
}
|
||||
p.mu.Unlock()
|
||||
|
||||
log.Printf("[Poller] Poll scan complete for mount %s: %d files", mountID, fileCount)
|
||||
}
|
||||
|
||||
// TriggerScan triggers an immediate scan for a mount
|
||||
func (p *Poller) TriggerScan(mountID, mountPath string) (int, error) {
|
||||
if p.scanFunc == nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
fileCount, err := p.scanFunc(mountPath)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Update status
|
||||
p.mu.Lock()
|
||||
if status, exists := p.status[mountID]; exists {
|
||||
status.LastScan = NowMS()
|
||||
} else {
|
||||
p.status[mountID] = &PollingStatus{
|
||||
LastScan: NowMS(),
|
||||
}
|
||||
}
|
||||
p.mu.Unlock()
|
||||
|
||||
return fileCount, nil
|
||||
}
|
||||
|
||||
// GetPollingStatus returns the polling status for a mount
|
||||
func (p *Poller) GetPollingStatus(mountID string) *PollingStatus {
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
|
||||
if status, exists := p.status[mountID]; exists {
|
||||
return &PollingStatus{
|
||||
Active: status.Active,
|
||||
LastScan: status.LastScan,
|
||||
}
|
||||
}
|
||||
return &PollingStatus{}
|
||||
}
|
||||
|
||||
// NotifyMountChanged notifies the poller that a mount's state has changed
|
||||
func (p *Poller) NotifyMountChanged() {
|
||||
go p.syncWithMounts()
|
||||
}
|
||||
@@ -11,6 +11,12 @@ const (
|
||||
MountTypeRclone MountType = "rclone"
|
||||
)
|
||||
|
||||
// Polling constants
|
||||
const (
|
||||
DefaultPollingIntervalMs = 30000 // 30 seconds
|
||||
MinPollingIntervalMs = 5000 // 5 seconds
|
||||
)
|
||||
|
||||
// MountConfig represents a mount configuration
|
||||
type MountConfig struct {
|
||||
// Common fields
|
||||
@@ -36,14 +42,20 @@ type MountConfig struct {
|
||||
// rclone-specific fields
|
||||
RcloneRemote string `json:"rcloneRemote,omitempty"`
|
||||
RclonePath string `json:"rclonePath,omitempty"`
|
||||
|
||||
// Polling configuration
|
||||
PollingEnabled bool `json:"pollingEnabled"`
|
||||
PollingIntervalMs int `json:"pollingIntervalMs,omitempty"` // Default: 30000
|
||||
}
|
||||
|
||||
// MountStatus represents the runtime status of a mount
|
||||
type MountStatus struct {
|
||||
MountConfig
|
||||
Mounted bool `json:"mounted"`
|
||||
Error string `json:"error,omitempty"`
|
||||
LastChecked int64 `json:"lastChecked"`
|
||||
Mounted bool `json:"mounted"`
|
||||
Error string `json:"error,omitempty"`
|
||||
LastChecked int64 `json:"lastChecked"`
|
||||
PollingActive bool `json:"pollingActive,omitempty"`
|
||||
LastPolledScan int64 `json:"lastPolledScan,omitempty"`
|
||||
}
|
||||
|
||||
// MountsFile represents the mounts.json file structure
|
||||
@@ -79,6 +91,19 @@ type CreateMountRequest struct {
|
||||
// rclone
|
||||
RcloneRemote string `json:"rcloneRemote,omitempty"`
|
||||
RclonePath string `json:"rclonePath,omitempty"`
|
||||
|
||||
// Polling
|
||||
PollingEnabled *bool `json:"pollingEnabled,omitempty"`
|
||||
PollingIntervalMs *int `json:"pollingIntervalMs,omitempty"`
|
||||
}
|
||||
|
||||
// MountScanResponse is the response for mount scan operations
|
||||
type MountScanResponse struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message,omitempty"`
|
||||
MountID string `json:"mountId"`
|
||||
MountPath string `json:"mountPath"`
|
||||
FileCount int `json:"fileCount,omitempty"`
|
||||
}
|
||||
|
||||
// MountResponse is the response for mount operations
|
||||
|
||||
Reference in New Issue
Block a user