Cleanup and improvements
[src/xds/xds-server.git] / lib / model / folders.go
index 02c3254..24ac48c 100644 (file)
@@ -7,13 +7,13 @@ import (
        "os"
        "path/filepath"
        "strings"
+       "time"
 
        "github.com/Sirupsen/logrus"
        common "github.com/iotbzh/xds-common/golib"
        "github.com/iotbzh/xds-server/lib/folder"
        "github.com/iotbzh/xds-server/lib/syncthing"
        "github.com/iotbzh/xds-server/lib/xdsconfig"
-       uuid "github.com/satori/go.uuid"
        "github.com/syncthing/syncthing/lib/sync"
 )
 
@@ -24,6 +24,12 @@ type Folders struct {
        Log        *logrus.Logger
        SThg       *st.SyncThing
        folders    map[string]*folder.IFOLDER
+       registerCB []RegisteredCB
+}
+
+type RegisteredCB struct {
+       cb   *folder.EventCB
+       data *folder.EventCBData
 }
 
 // Mutex to make add/delete atomic
@@ -39,6 +45,7 @@ func FoldersNew(cfg *xdsconfig.Config, st *st.SyncThing) *Folders {
                Log:        cfg.Log,
                SThg:       st,
                folders:    make(map[string]*folder.IFOLDER),
+               registerCB: []RegisteredCB{},
        }
 }
 
@@ -71,6 +78,8 @@ func (f *Folders) LoadConfig() error {
                        // Don't exit on such error, just log it
                        f.Log.Errorf(err.Error())
                }
+       } else {
+               f.Log.Infof("Syncthing support is disabled.")
        }
 
        // Merge syncthing folders into XDS folders
@@ -94,32 +103,37 @@ func (f *Folders) LoadConfig() error {
 
        // Detect ghost project
        // (IOW existing in xds file config and not in syncthing database)
-       for i, xf := range flds {
-               // only for syncthing project
-               if xf.Type != folder.TypeCloudSync {
-                       continue
-               }
-               found := false
-               for _, stf := range stFlds {
-                       if stf.ID == xf.ID {
-                               found = true
-                               break
+       if f.SThg != nil {
+               for i, xf := range flds {
+                       // only for syncthing project
+                       if xf.Type != folder.TypeCloudSync {
+                               continue
+                       }
+                       found := false
+                       for _, stf := range stFlds {
+                               if stf.ID == xf.ID {
+                                       found = true
+                                       break
+                               }
+                       }
+                       if !found {
+                               flds[i].Status = folder.StatusErrorConfig
                        }
-               }
-               if !found {
-                       flds[i].Status = folder.StatusErrorConfig
                }
        }
 
        // Update folders
        f.Log.Infof("Loading initial folders config: %d folders found", len(flds))
        for _, fc := range flds {
-               if _, err := f.createUpdate(fc, false); err != nil {
+               if _, err := f.createUpdate(fc, false, true); err != nil {
                        return err
                }
        }
 
-       return nil
+       // Save config on disk
+       err := f.SaveConfig()
+
+       return err
 }
 
 // SaveConfig Save folders configuration to disk
@@ -154,8 +168,7 @@ func (f *Folders) GetConfigArr() []folder.FolderConfig {
 
 // getConfigArrUnsafe Same as GetConfigArr without mutex protection
 func (f *Folders) getConfigArrUnsafe() []folder.FolderConfig {
-       var conf []folder.FolderConfig
-
+       conf := []folder.FolderConfig{}
        for _, v := range f.folders {
                conf = append(conf, (*v).GetConfig())
        }
@@ -164,11 +177,11 @@ func (f *Folders) getConfigArrUnsafe() []folder.FolderConfig {
 
 // Add adds a new folder
 func (f *Folders) Add(newF folder.FolderConfig) (*folder.FolderConfig, error) {
-       return f.createUpdate(newF, true)
+       return f.createUpdate(newF, true, false)
 }
 
 // CreateUpdate creates or update a folder
-func (f *Folders) createUpdate(newF folder.FolderConfig, create bool) (*folder.FolderConfig, error) {
+func (f *Folders) createUpdate(newF folder.FolderConfig, create bool, initial bool) (*folder.FolderConfig, error) {
 
        fcMutex.Lock()
        defer fcMutex.Unlock()
@@ -181,36 +194,43 @@ func (f *Folders) createUpdate(newF folder.FolderConfig, create bool) (*folder.F
                return nil, fmt.Errorf("ClientPath must be set")
        }
 
-       // Allocate a new UUID
-       if create {
-               newF.ID = uuid.NewV1().String()
-       }
-       if !create && newF.ID == "" {
-               return nil, fmt.Errorf("Cannot update folder with null ID")
+       // Create a new folder object
+       var fld folder.IFOLDER
+       switch newF.Type {
+       // SYNCTHING
+       case folder.TypeCloudSync:
+               if f.SThg != nil {
+                       fld = f.SThg.NewFolderST(f.Conf)
+               } else {
+                       f.Log.Debugf("Disable project %v (syncthing not initialized)", newF.ID)
+                       fld = folder.NewFolderSTDisable(f.Conf)
+               }
+
+       // PATH MAP
+       case folder.TypePathMap:
+               fld = folder.NewFolderPathMap(f.Conf)
+       default:
+               return nil, fmt.Errorf("Unsupported folder type")
        }
 
        // Set default value if needed
        if newF.Status == "" {
                newF.Status = folder.StatusDisable
        }
-
        if newF.Label == "" {
                newF.Label = filepath.Base(newF.ClientPath) + "_" + newF.ID[0:8]
        }
 
-       var fld folder.IFOLDER
-       switch newF.Type {
-       // SYNCTHING
-       case folder.TypeCloudSync:
-               if f.SThg == nil {
-                       return nil, fmt.Errorf("ClownSync type not supported (syncthing not initialized)")
+       // Allocate a new UUID
+       if create {
+               i := len(newF.Label)
+               if i > 20 {
+                       i = 20
                }
-               fld = f.SThg.NewFolderST(f.Conf)
-       // PATH MAP
-       case folder.TypePathMap:
-               fld = folder.NewFolderPathMap(f.Conf)
-       default:
-               return nil, fmt.Errorf("Unsupported folder type")
+               newF.ID = fld.NewUID(newF.Label[:i])
+       }
+       if !create && newF.ID == "" {
+               return nil, fmt.Errorf("Cannot update folder with null ID")
        }
 
        // Normalize path (needed for Windows path including bashlashes)
@@ -224,13 +244,31 @@ func (f *Folders) createUpdate(newF folder.FolderConfig, create bool) (*folder.F
                return newFolder, err
        }
 
-       // Register folder object
+       // Add to folders list
        f.folders[newF.ID] = &fld
 
        // Save config on disk
-       err = f.SaveConfig()
+       if !initial {
+               if err := f.SaveConfig(); err != nil {
+                       return newFolder, err
+               }
+       }
+
+       // Register event change callback
+       for _, rcb := range f.registerCB {
+               if err := fld.RegisterEventChange(rcb.cb, rcb.data); err != nil {
+                       return newFolder, err
+               }
+       }
+
+       // Force sync after creation
+       // (need to defer to be sure that WS events will arrive after HTTP creation reply)
+       go func() {
+               time.Sleep(time.Millisecond * 500)
+               fld.Sync()
+       }()
 
-       return newFolder, err
+       return newFolder, nil
 }
 
 // Delete deletes a specific folder
@@ -260,6 +298,29 @@ func (f *Folders) Delete(id string) (folder.FolderConfig, error) {
        return fld, err
 }
 
+// RegisterEventChange requests registration for folder event change
+func (f *Folders) RegisterEventChange(id string, cb *folder.EventCB, data *folder.EventCBData) error {
+
+       flds := make(map[string]*folder.IFOLDER)
+       if id != "" {
+               // Register to a specific folder
+               flds[id] = f.Get(id)
+       } else {
+               // Register to all folders
+               flds = f.folders
+               f.registerCB = append(f.registerCB, RegisteredCB{cb: cb, data: data})
+       }
+
+       for _, fld := range flds {
+               err := (*fld).RegisterEventChange(cb, data)
+               if err != nil {
+                       return err
+               }
+       }
+
+       return nil
+}
+
 // ForceSync Force the synchronization of a folder
 func (f *Folders) ForceSync(id string) error {
        fc := f.Get(id)