Added webapp Dashboard + logic to interact with server.
[src/xds/xds-agent.git] / lib / agent / projects.go
1 package agent
2
3 import (
4         "fmt"
5         "log"
6         "time"
7
8         "github.com/iotbzh/xds-agent/lib/syncthing"
9         "github.com/syncthing/syncthing/lib/sync"
10 )
11
12 // Projects Represent a an XDS Projects
13 type Projects struct {
14         *Context
15         SThg     *st.SyncThing
16         projects map[string]*IPROJECT
17         //SEB registerCB []RegisteredCB
18 }
19
20 /* SEB
21 type RegisteredCB struct {
22         cb   *EventCB
23         data *EventCBData
24 }
25 */
26
27 // Mutex to make add/delete atomic
28 var pjMutex = sync.NewMutex()
29
30 // NewProjects Create a new instance of Project Model
31 func NewProjects(ctx *Context, st *st.SyncThing) *Projects {
32         return &Projects{
33                 Context:  ctx,
34                 SThg:     st,
35                 projects: make(map[string]*IPROJECT),
36                 //registerCB: []RegisteredCB{},
37         }
38 }
39
40 // Init Load Projects configuration
41 func (p *Projects) Init(server *XdsServer) error {
42         svrList := make(map[string]*XdsServer)
43         // If server not set, load for all servers
44         if server == nil {
45                 svrList = p.xdsServers
46         } else {
47                 svrList[server.ID] = server
48         }
49         errMsg := ""
50         for _, svr := range svrList {
51                 if svr.Disabled {
52                         continue
53                 }
54                 xFlds := []FolderConfig{}
55                 if err := svr.HTTPGet("/folders", &xFlds); err != nil {
56                         errMsg += fmt.Sprintf("Cannot retrieve folders config of XDS server ID %s : %v \n", svr.ID, err.Error())
57                         continue
58                 }
59                 p.Log.Debugf("Server %s, %d projects detected", svr.ID[:8], len(xFlds))
60                 for _, prj := range xFlds {
61                         newP := svr.FolderToProject(prj)
62                         if /*nPrj*/ _, err := p.createUpdate(newP, false, true); err != nil {
63                                 errMsg += "Error while creating project id " + prj.ID + ": " + err.Error() + "\n "
64                                 continue
65                         }
66
67                         /* FIXME emit EVTProjectChange event ?
68                         if err := p.events.Emit(EVTProjectChange, *nPrj); err != nil {
69                                 p.Log.Warningf("Cannot notify project change: %v", err)
70                         }
71                         */
72                 }
73
74         }
75
76         p.Log.Infof("Number of loaded Projects: %d", len(p.projects))
77
78         if errMsg != "" {
79                 return fmt.Errorf(errMsg)
80         }
81         return nil
82 }
83
84 // Get returns the folder config or nil if not existing
85 func (p *Projects) Get(id string) *IPROJECT {
86         if id == "" {
87                 return nil
88         }
89         fc, exist := p.projects[id]
90         if !exist {
91                 return nil
92         }
93         return fc
94 }
95
96 // GetProjectArr returns the config of all folders as an array
97 func (p *Projects) GetProjectArr() []ProjectConfig {
98         pjMutex.Lock()
99         defer pjMutex.Unlock()
100
101         return p.GetProjectArrUnsafe()
102 }
103
104 // GetProjectArrUnsafe Same as GetProjectArr without mutex protection
105 func (p *Projects) GetProjectArrUnsafe() []ProjectConfig {
106         conf := []ProjectConfig{}
107         for _, v := range p.projects {
108                 prj := (*v).GetProject()
109                 conf = append(conf, *prj)
110         }
111         return conf
112 }
113
114 // Add adds a new folder
115 func (p *Projects) Add(newF ProjectConfig) (*ProjectConfig, error) {
116         prj, err := p.createUpdate(newF, true, false)
117         if err != nil {
118                 return prj, err
119         }
120
121         // Notify client with event
122         if err := p.events.Emit(EVTProjectAdd, *prj); err != nil {
123                 p.Log.Warningf("Cannot notify project deletion: %v", err)
124         }
125
126         return prj, err
127 }
128
129 // CreateUpdate creates or update a folder
130 func (p *Projects) createUpdate(newF ProjectConfig, create bool, initial bool) (*ProjectConfig, error) {
131         var err error
132
133         pjMutex.Lock()
134         defer pjMutex.Unlock()
135
136         // Sanity check
137         if _, exist := p.projects[newF.ID]; create && exist {
138                 return nil, fmt.Errorf("ID already exists")
139         }
140         if newF.ClientPath == "" {
141                 return nil, fmt.Errorf("ClientPath must be set")
142         }
143         if newF.ServerID == "" {
144                 return nil, fmt.Errorf("Server ID must be set")
145         }
146         var svr *XdsServer
147         var exist bool
148         if svr, exist = p.xdsServers[newF.ServerID]; !exist {
149                 return nil, fmt.Errorf("Unknown Server ID %s", newF.ServerID)
150         }
151
152         // Check type supported
153         b, exist := svr.ServerConfig.SupportedSharing[string(newF.Type)]
154         if !exist || !b {
155                 return nil, fmt.Errorf("Server doesn't support project type %s", newF.Type)
156         }
157
158         // Create a new folder object
159         var fld IPROJECT
160         switch newF.Type {
161         // SYNCTHING
162         case TypeCloudSync:
163                 if p.SThg != nil {
164                         /*SEB fld = f.SThg.NewFolderST(f.Conf)*/
165                         fld = NewProjectST(p.Context, svr)
166                 } else {
167                         return nil, fmt.Errorf("Cloud Sync project not supported")
168                 }
169
170         // PATH MAP
171         case TypePathMap:
172                 fld = NewProjectPathMap(p.Context, svr)
173         default:
174                 return nil, fmt.Errorf("Unsupported folder type")
175         }
176
177         var newPrj *ProjectConfig
178         if create {
179                 // Add project on server
180                 if newPrj, err = fld.Add(newF); err != nil {
181                         newF.Status = StatusErrorConfig
182                         log.Printf("ERROR Adding folder: %v\n", err)
183                         return newPrj, err
184                 }
185         } else {
186                 // Just update project config
187                 newPrj = fld.SetProject(newF)
188         }
189
190         // Sanity check
191         if newPrj.ID == "" {
192                 log.Printf("ERROR project ID empty: %v", newF)
193                 return newPrj, fmt.Errorf("Project ID empty")
194         }
195
196         // Add to folders list
197         p.projects[newPrj.ID] = &fld
198
199         // Force sync after creation
200         // (need to defer to be sure that WS events will arrive after HTTP creation reply)
201         go func() {
202                 time.Sleep(time.Millisecond * 500)
203                 fld.Sync()
204         }()
205
206         return newPrj, nil
207 }
208
209 // Delete deletes a specific folder
210 func (p *Projects) Delete(id string) (ProjectConfig, error) {
211         var err error
212
213         pjMutex.Lock()
214         defer pjMutex.Unlock()
215
216         fld := ProjectConfig{}
217         fc, exist := p.projects[id]
218         if !exist {
219                 return fld, fmt.Errorf("unknown id")
220         }
221
222         prj := (*fc).GetProject()
223
224         if err = (*fc).Delete(); err != nil {
225                 return *prj, err
226         }
227
228         delete(p.projects, id)
229
230         // Notify client with event
231         if err := p.events.Emit(EVTProjectDelete, *prj); err != nil {
232                 p.Log.Warningf("Cannot notify project deletion: %v", err)
233         }
234
235         return *prj, err
236 }
237
238 // ForceSync Force the synchronization of a folder
239 func (p *Projects) ForceSync(id string) error {
240         fc := p.Get(id)
241         if fc == nil {
242                 return fmt.Errorf("Unknown id")
243         }
244         return (*fc).Sync()
245 }
246
247 // IsProjectInSync Returns true when folder is in sync
248 func (p *Projects) IsProjectInSync(id string) (bool, error) {
249         fc := p.Get(id)
250         if fc == nil {
251                 return false, fmt.Errorf("Unknown id")
252         }
253         return (*fc).IsInSync()
254 }