summaryrefslogtreecommitdiffstats
path: root/lib/model/folders.go
diff options
context:
space:
mode:
Diffstat (limited to 'lib/model/folders.go')
-rw-r--r--lib/model/folders.go463
1 files changed, 0 insertions, 463 deletions
diff --git a/lib/model/folders.go b/lib/model/folders.go
deleted file mode 100644
index 0e28538..0000000
--- a/lib/model/folders.go
+++ /dev/null
@@ -1,463 +0,0 @@
-package model
-
-import (
- "encoding/xml"
- "fmt"
- "log"
- "os"
- "path/filepath"
- "strings"
- "time"
-
- "github.com/Sirupsen/logrus"
- "github.com/franciscocpg/reflectme"
- 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"
- "github.com/syncthing/syncthing/lib/sync"
-)
-
-// Folders Represent a an XDS folders
-type Folders struct {
- fileOnDisk string
- Conf *xdsconfig.Config
- 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
-var fcMutex = sync.NewMutex()
-var ffMutex = sync.NewMutex()
-
-// FoldersNew Create a new instance of Model Folders
-func FoldersNew(cfg *xdsconfig.Config, st *st.SyncThing) *Folders {
- file, _ := xdsconfig.FoldersConfigFilenameGet()
- return &Folders{
- fileOnDisk: file,
- Conf: cfg,
- Log: cfg.Log,
- SThg: st,
- folders: make(map[string]*folder.IFOLDER),
- registerCB: []RegisteredCB{},
- }
-}
-
-// LoadConfig Load folders configuration from disk
-func (f *Folders) LoadConfig() error {
- var flds []folder.FolderConfig
- var stFlds []folder.FolderConfig
-
- // load from disk
- if f.Conf.Options.NoFolderConfig {
- f.Log.Infof("Don't read folder config file (-no-folderconfig option is set)")
- } else if f.fileOnDisk != "" {
- f.Log.Infof("Use folder config file: %s", f.fileOnDisk)
- err := foldersConfigRead(f.fileOnDisk, &flds)
- if err != nil {
- if strings.HasPrefix(err.Error(), "No folder config") {
- f.Log.Warnf(err.Error())
- } else {
- return err
- }
- }
- } else {
- f.Log.Warnf("Folders config filename not set")
- }
-
- // Retrieve initial Syncthing config (just append don't overwrite existing ones)
- if f.SThg != nil {
- f.Log.Infof("Retrieve syncthing folder config")
- if err := f.SThg.FolderLoadFromStConfig(&stFlds); err != nil {
- // 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
- for _, stf := range stFlds {
- found := false
- for i, xf := range flds {
- if xf.ID == stf.ID {
- found = true
- // sanity check
- if xf.Type != folder.TypeCloudSync {
- flds[i].Status = folder.StatusErrorConfig
- }
- break
- }
- }
- // add it
- if !found {
- flds = append(flds, stf)
- }
- }
-
- // Detect ghost project
- // (IOW existing in xds file config and not in syncthing database)
- 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
- }
- }
- }
-
- // Update folders
- f.Log.Infof("Loading initial folders config: %d folders found", len(flds))
- for _, fc := range flds {
- if _, err := f.createUpdate(fc, false, true); err != nil {
- return err
- }
- }
-
- // Save config on disk
- err := f.SaveConfig()
-
- return err
-}
-
-// SaveConfig Save folders configuration to disk
-func (f *Folders) SaveConfig() error {
- if f.fileOnDisk == "" {
- return fmt.Errorf("Folders config filename not set")
- }
-
- // FIXME: buffered save or avoid to write on disk each time
- return foldersConfigWrite(f.fileOnDisk, f.getConfigArrUnsafe())
-}
-
-// ResolveID Complete a Folder ID (helper for user that can use partial ID value)
-func (f *Folders) ResolveID(id string) (string, error) {
- if id == "" {
- return "", nil
- }
-
- match := []string{}
- for iid := range f.folders {
- if strings.HasPrefix(iid, id) {
- match = append(match, iid)
- }
- }
-
- if len(match) == 1 {
- return match[0], nil
- } else if len(match) == 0 {
- return id, fmt.Errorf("Unknown id")
- }
- return id, fmt.Errorf("Multiple IDs found with provided prefix: " + id)
-}
-
-// Get returns the folder config or nil if not existing
-func (f *Folders) Get(id string) *folder.IFOLDER {
- if id == "" {
- return nil
- }
- fc, exist := f.folders[id]
- if !exist {
- return nil
- }
- return fc
-}
-
-// GetConfigArr returns the config of all folders as an array
-func (f *Folders) GetConfigArr() []folder.FolderConfig {
- fcMutex.Lock()
- defer fcMutex.Unlock()
-
- return f.getConfigArrUnsafe()
-}
-
-// getConfigArrUnsafe Same as GetConfigArr without mutex protection
-func (f *Folders) getConfigArrUnsafe() []folder.FolderConfig {
- conf := []folder.FolderConfig{}
- for _, v := range f.folders {
- conf = append(conf, (*v).GetConfig())
- }
- return conf
-}
-
-// Add adds a new folder
-func (f *Folders) Add(newF folder.FolderConfig) (*folder.FolderConfig, error) {
- return f.createUpdate(newF, true, false)
-}
-
-// CreateUpdate creates or update a folder
-func (f *Folders) createUpdate(newF folder.FolderConfig, create bool, initial bool) (*folder.FolderConfig, error) {
-
- fcMutex.Lock()
- defer fcMutex.Unlock()
-
- // Sanity check
- if _, exist := f.folders[newF.ID]; create && exist {
- return nil, fmt.Errorf("ID already exists")
- }
- if newF.ClientPath == "" {
- return nil, fmt.Errorf("ClientPath must be set")
- }
-
- // 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")
- }
-
- // Allocate a new UUID
- if create {
- newF.ID = fld.NewUID("")
- }
- if !create && newF.ID == "" {
- return nil, fmt.Errorf("Cannot update folder with null ID")
- }
-
- // Set default value if needed
- if newF.Status == "" {
- newF.Status = folder.StatusDisable
- }
- if newF.Label == "" {
- newF.Label = filepath.Base(newF.ClientPath)
- if len(newF.ID) > 8 {
- newF.Label += "_" + newF.ID[0:8]
- }
- }
-
- // Normalize path (needed for Windows path including bashlashes)
- newF.ClientPath = common.PathNormalize(newF.ClientPath)
-
- // Add new folder
- newFolder, err := fld.Add(newF)
- if err != nil {
- newF.Status = folder.StatusErrorConfig
- log.Printf("ERROR Adding folder: %v\n", err)
- return newFolder, err
- }
-
- // Add to folders list
- f.folders[newF.ID] = &fld
-
- // Save config on disk
- 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, nil
-}
-
-// Delete deletes a specific folder
-func (f *Folders) Delete(id string) (folder.FolderConfig, error) {
- var err error
-
- fcMutex.Lock()
- defer fcMutex.Unlock()
-
- fld := folder.FolderConfig{}
- fc, exist := f.folders[id]
- if !exist {
- return fld, fmt.Errorf("unknown id")
- }
-
- fld = (*fc).GetConfig()
-
- if err = (*fc).Remove(); err != nil {
- return fld, err
- }
-
- delete(f.folders, id)
-
- // Save config on disk
- err = f.SaveConfig()
-
- return fld, err
-}
-
-// Update Update a specific folder
-func (f *Folders) Update(id string, cfg folder.FolderConfig) (*folder.FolderConfig, error) {
- fcMutex.Lock()
- defer fcMutex.Unlock()
-
- fc, exist := f.folders[id]
- if !exist {
- return nil, fmt.Errorf("unknown id")
- }
-
- // Copy current in a new object to change nothing in case of an error rises
- newCfg := folder.FolderConfig{}
- reflectme.Copy((*fc).GetConfig(), &newCfg)
-
- // Only update some fields
- dirty := false
- for _, fieldName := range folder.FolderConfigUpdatableFields {
- valNew, err := reflectme.GetField(cfg, fieldName)
- if err == nil {
- valCur, err := reflectme.GetField(newCfg, fieldName)
- if err == nil && valNew != valCur {
- err = reflectme.SetField(&newCfg, fieldName, valNew)
- if err != nil {
- return nil, err
- }
- dirty = true
- }
- }
- }
-
- if !dirty {
- return &newCfg, nil
- }
-
- fld, err := (*fc).Update(newCfg)
- if err != nil {
- return fld, err
- }
-
- // Save config on disk
- err = f.SaveConfig()
-
- // Send event to notified changes
- // TODO emit folder change event
-
- 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)
- if fc == nil {
- return fmt.Errorf("Unknown id")
- }
- return (*fc).Sync()
-}
-
-// IsFolderInSync Returns true when folder is in sync
-func (f *Folders) IsFolderInSync(id string) (bool, error) {
- fc := f.Get(id)
- if fc == nil {
- return false, fmt.Errorf("Unknown id")
- }
- return (*fc).IsInSync()
-}
-
-//*** Private functions ***
-
-// Use XML format and not json to be able to save/load all fields including
-// ones that are masked in json (IOW defined with `json:"-"`)
-type xmlFolders struct {
- XMLName xml.Name `xml:"folders"`
- Version string `xml:"version,attr"`
- Folders []folder.FolderConfig `xml:"folders"`
-}
-
-// foldersConfigRead reads folders config from disk
-func foldersConfigRead(file string, folders *[]folder.FolderConfig) error {
- if !common.Exists(file) {
- return fmt.Errorf("No folder config file found (%s)", file)
- }
-
- ffMutex.Lock()
- defer ffMutex.Unlock()
-
- fd, err := os.Open(file)
- defer fd.Close()
- if err != nil {
- return err
- }
-
- data := xmlFolders{}
- err = xml.NewDecoder(fd).Decode(&data)
- if err == nil {
- *folders = data.Folders
- }
- return err
-}
-
-// foldersConfigWrite writes folders config on disk
-func foldersConfigWrite(file string, folders []folder.FolderConfig) error {
- ffMutex.Lock()
- defer ffMutex.Unlock()
-
- fd, err := os.OpenFile(file, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
- defer fd.Close()
- if err != nil {
- return err
- }
-
- data := &xmlFolders{
- Version: "1",
- Folders: folders,
- }
-
- enc := xml.NewEncoder(fd)
- enc.Indent("", " ")
- return enc.Encode(data)
-}