New dashboard improvements.
[src/xds/xds-agent.git] / lib / agent / project-st.go
1 package agent
2
3 import (
4         "fmt"
5
6         "github.com/iotbzh/xds-agent/lib/apiv1"
7         st "github.com/iotbzh/xds-agent/lib/syncthing"
8 )
9
10 // IPROJECT interface implementation for syncthing projects
11
12 // STProject .
13 type STProject struct {
14         *Context
15         server   *XdsServer
16         folder   *XdsFolderConfig
17         eventIDs []int
18 }
19
20 // NewProjectST Create a new instance of STProject
21 func NewProjectST(ctx *Context, svr *XdsServer) *STProject {
22         p := STProject{
23                 Context: ctx,
24                 server:  svr,
25                 folder:  &XdsFolderConfig{},
26         }
27         return &p
28 }
29
30 // Add a new project
31 func (p *STProject) Add(cfg apiv1.ProjectConfig) (*apiv1.ProjectConfig, error) {
32         var err error
33
34         // Add project/folder into XDS Server
35         err = p.server.FolderAdd(p.server.ProjectToFolder(cfg), p.folder)
36         if err != nil {
37                 return nil, err
38         }
39         svrPrj := p.GetProject()
40
41         // Declare project into local Syncthing
42         id, err := p.SThg.FolderChange(st.FolderChangeArg{
43                 ID:           svrPrj.ID,
44                 Label:        svrPrj.Label,
45                 RelativePath: cfg.ClientPath,
46                 SyncThingID:  p.server.ServerConfig.Builder.SyncThingID,
47         })
48         if err != nil {
49                 return nil, err
50         }
51
52         locPrj, err := p.SThg.FolderConfigGet(id)
53         if err != nil {
54                 svrPrj.Status = apiv1.StatusErrorConfig
55                 return nil, err
56         }
57         if svrPrj.ID != locPrj.ID {
58                 p.Log.Errorf("Project ID in XDSServer and local ST differ: %s != %s", svrPrj.ID, locPrj.ID)
59         }
60
61         // Use Setup function to setup remains fields
62         return p.Setup(*svrPrj)
63 }
64
65 // Delete a project
66 func (p *STProject) Delete() error {
67         errSvr := p.server.FolderDelete(p.folder.ID)
68         errLoc := p.SThg.FolderDelete(p.folder.ID)
69         if errSvr != nil {
70                 return errSvr
71         }
72         return errLoc
73 }
74
75 // GetProject Get public part of project config
76 func (p *STProject) GetProject() *apiv1.ProjectConfig {
77         prj := p.server.FolderToProject(*p.folder)
78         prj.ServerID = p.server.ID
79         return &prj
80 }
81
82 // Setup Setup local project config
83 func (p *STProject) Setup(prj apiv1.ProjectConfig) (*apiv1.ProjectConfig, error) {
84         // Update folder
85         p.folder = p.server.ProjectToFolder(prj)
86         svrPrj := p.GetProject()
87
88         // Register events to update folder status
89         // Register to XDS Server events
90         p.server.EventOn("event:folder-state-change", "", p._cbServerFolderChanged)
91         if err := p.server.EventRegister("folder-state-change", svrPrj.ID); err != nil {
92                 p.Log.Warningf("XDS Server EventRegister failed: %v", err)
93                 return svrPrj, err
94         }
95
96         // Register to Local Syncthing events
97         for _, evName := range []string{st.EventStateChanged, st.EventFolderPaused} {
98                 evID, err := p.SThg.Events.Register(evName, p._cbLocalSTEvents, svrPrj.ID, nil)
99                 if err != nil {
100                         return nil, err
101                 }
102                 p.eventIDs = append(p.eventIDs, evID)
103         }
104
105         return svrPrj, nil
106 }
107
108 // Update Update some field of a project
109 func (p *STProject) Update(prj apiv1.ProjectConfig) (*apiv1.ProjectConfig, error) {
110
111         if p.folder.ID != prj.ID {
112                 return nil, fmt.Errorf("Invalid id")
113         }
114
115         err := p.server.FolderUpdate(p.server.ProjectToFolder(prj), p.folder)
116         if err != nil {
117                 return nil, err
118         }
119
120         return p.GetProject(), nil
121 }
122
123 // GetServer Get the XdsServer that holds this project
124 func (p *STProject) GetServer() *XdsServer {
125         return p.server
126 }
127
128 // Sync Force project files synchronization
129 func (p *STProject) Sync() error {
130         if err := p.server.FolderSync(p.folder.ID); err != nil {
131                 return err
132         }
133         return p.SThg.FolderScan(p.folder.ID, "")
134 }
135
136 // IsInSync Check if project files are in-sync
137 func (p *STProject) IsInSync() (bool, error) {
138         // Should be up-to-date by callbacks (see below)
139         return p.folder.IsInSync, nil
140 }
141
142 /**
143 ** Private functions
144 ***/
145
146 // callback use to update (XDS Server) folder IsInSync status
147
148 func (p *STProject) _cbServerFolderChanged(pData interface{}, data interface{}) error {
149         evt := data.(XdsEventFolderChange)
150
151         // Only process event that concerns this project/folder ID
152         if p.folder.ID != evt.Folder.ID {
153                 return nil
154         }
155
156         if evt.Folder.IsInSync != p.folder.DataCloudSync.STSvrIsInSync ||
157                 evt.Folder.Status != p.folder.DataCloudSync.STSvrStatus {
158
159                 p.folder.DataCloudSync.STSvrIsInSync = evt.Folder.IsInSync
160                 p.folder.DataCloudSync.STSvrStatus = evt.Folder.Status
161
162                 if err := p.events.Emit(apiv1.EVTProjectChange, p.server.FolderToProject(*p.folder), ""); err != nil {
163                         p.Log.Warningf("Cannot notify project change (from server): %v", err)
164                 }
165         }
166         return nil
167 }
168
169 // callback use to update IsInSync status
170 func (p *STProject) _cbLocalSTEvents(ev st.Event, data *st.EventsCBData) {
171
172         inSync := p.folder.DataCloudSync.STLocIsInSync
173         sts := p.folder.DataCloudSync.STLocStatus
174         prevSync := inSync
175         prevStatus := sts
176
177         switch ev.Type {
178
179         case st.EventStateChanged:
180                 to := ev.Data["to"]
181                 switch to {
182                 case "scanning", "syncing":
183                         sts = apiv1.StatusSyncing
184                 case "idle":
185                         sts = apiv1.StatusEnable
186                 }
187                 inSync = (to == "idle")
188
189         case st.EventFolderPaused:
190                 if sts == apiv1.StatusEnable {
191                         sts = apiv1.StatusPause
192                 }
193                 inSync = false
194         }
195
196         if prevSync != inSync || prevStatus != sts {
197
198                 p.folder.DataCloudSync.STLocIsInSync = inSync
199                 p.folder.DataCloudSync.STLocStatus = sts
200
201                 if err := p.events.Emit(apiv1.EVTProjectChange, p.server.FolderToProject(*p.folder), ""); err != nil {
202                         p.Log.Warningf("Cannot notify project change (local): %v", err)
203                 }
204         }
205 }