Add API service for local disk changes
GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/3626 LGTM: calmh, AudriusButkevicius
This commit is contained in:
parent
50190236bb
commit
d322ebd0b9
@ -53,6 +53,7 @@ type apiService struct {
|
|||||||
statics *staticsServer
|
statics *staticsServer
|
||||||
model modelIntf
|
model modelIntf
|
||||||
eventSub events.BufferedSubscription
|
eventSub events.BufferedSubscription
|
||||||
|
diskEventSub events.BufferedSubscription
|
||||||
discoverer discover.CachingMux
|
discoverer discover.CachingMux
|
||||||
connectionsService connectionsIntf
|
connectionsService connectionsIntf
|
||||||
fss *folderSummaryService
|
fss *folderSummaryService
|
||||||
@ -113,7 +114,7 @@ type connectionsIntf interface {
|
|||||||
Status() map[string]interface{}
|
Status() map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAPIService(id protocol.DeviceID, cfg configIntf, httpsCertFile, httpsKeyFile, assetDir string, m modelIntf, eventSub events.BufferedSubscription, discoverer discover.CachingMux, connectionsService connectionsIntf, errors, systemLog logger.Recorder) *apiService {
|
func newAPIService(id protocol.DeviceID, cfg configIntf, httpsCertFile, httpsKeyFile, assetDir string, m modelIntf, eventSub events.BufferedSubscription, diskEventSub events.BufferedSubscription, discoverer discover.CachingMux, connectionsService connectionsIntf, errors, systemLog logger.Recorder) *apiService {
|
||||||
service := &apiService{
|
service := &apiService{
|
||||||
id: id,
|
id: id,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
@ -122,6 +123,7 @@ func newAPIService(id protocol.DeviceID, cfg configIntf, httpsCertFile, httpsKey
|
|||||||
statics: newStaticsServer(cfg.GUI().Theme, assetDir),
|
statics: newStaticsServer(cfg.GUI().Theme, assetDir),
|
||||||
model: m,
|
model: m,
|
||||||
eventSub: eventSub,
|
eventSub: eventSub,
|
||||||
|
diskEventSub: diskEventSub,
|
||||||
discoverer: discoverer,
|
discoverer: discoverer,
|
||||||
connectionsService: connectionsService,
|
connectionsService: connectionsService,
|
||||||
systemConfigMut: sync.NewMutex(),
|
systemConfigMut: sync.NewMutex(),
|
||||||
@ -229,7 +231,8 @@ func (s *apiService) Serve() {
|
|||||||
getRestMux.HandleFunc("/rest/db/need", s.getDBNeed) // folder [perpage] [page]
|
getRestMux.HandleFunc("/rest/db/need", s.getDBNeed) // folder [perpage] [page]
|
||||||
getRestMux.HandleFunc("/rest/db/status", s.getDBStatus) // folder
|
getRestMux.HandleFunc("/rest/db/status", s.getDBStatus) // folder
|
||||||
getRestMux.HandleFunc("/rest/db/browse", s.getDBBrowse) // folder [prefix] [dirsonly] [levels]
|
getRestMux.HandleFunc("/rest/db/browse", s.getDBBrowse) // folder [prefix] [dirsonly] [levels]
|
||||||
getRestMux.HandleFunc("/rest/events", s.getEvents) // since [limit]
|
getRestMux.HandleFunc("/rest/events", s.getIndexEvents) // since [limit]
|
||||||
|
getRestMux.HandleFunc("/rest/events/disk", s.getDiskEvents) // since [limit]
|
||||||
getRestMux.HandleFunc("/rest/stats/device", s.getDeviceStats) // -
|
getRestMux.HandleFunc("/rest/stats/device", s.getDeviceStats) // -
|
||||||
getRestMux.HandleFunc("/rest/stats/folder", s.getFolderStats) // -
|
getRestMux.HandleFunc("/rest/stats/folder", s.getFolderStats) // -
|
||||||
getRestMux.HandleFunc("/rest/svc/deviceid", s.getDeviceID) // id
|
getRestMux.HandleFunc("/rest/svc/deviceid", s.getDeviceID) // id
|
||||||
@ -993,15 +996,22 @@ func (s *apiService) postDBIgnores(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.getDBIgnores(w, r)
|
s.getDBIgnores(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *apiService) getEvents(w http.ResponseWriter, r *http.Request) {
|
func (s *apiService) getIndexEvents(w http.ResponseWriter, r *http.Request) {
|
||||||
|
s.fss.gotEventRequest()
|
||||||
|
s.getEvents(w, r, s.eventSub)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *apiService) getDiskEvents(w http.ResponseWriter, r *http.Request) {
|
||||||
|
s.getEvents(w, r, s.diskEventSub)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *apiService) getEvents(w http.ResponseWriter, r *http.Request, eventSub events.BufferedSubscription) {
|
||||||
qs := r.URL.Query()
|
qs := r.URL.Query()
|
||||||
sinceStr := qs.Get("since")
|
sinceStr := qs.Get("since")
|
||||||
limitStr := qs.Get("limit")
|
limitStr := qs.Get("limit")
|
||||||
since, _ := strconv.Atoi(sinceStr)
|
since, _ := strconv.Atoi(sinceStr)
|
||||||
limit, _ := strconv.Atoi(limitStr)
|
limit, _ := strconv.Atoi(limitStr)
|
||||||
|
|
||||||
s.fss.gotEventRequest()
|
|
||||||
|
|
||||||
// Flush before blocking, to indicate that we've received the request and
|
// Flush before blocking, to indicate that we've received the request and
|
||||||
// that it should not be retried. Must set Content-Type header before
|
// that it should not be retried. Must set Content-Type header before
|
||||||
// flushing.
|
// flushing.
|
||||||
@ -1009,7 +1019,7 @@ func (s *apiService) getEvents(w http.ResponseWriter, r *http.Request) {
|
|||||||
f := w.(http.Flusher)
|
f := w.(http.Flusher)
|
||||||
f.Flush()
|
f.Flush()
|
||||||
|
|
||||||
evs := s.eventSub.Since(since, nil)
|
evs := eventSub.Since(since, nil)
|
||||||
if 0 < limit && limit < len(evs) {
|
if 0 < limit && limit < len(evs) {
|
||||||
evs = evs[len(evs)-limit:]
|
evs = evs[len(evs)-limit:]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -70,7 +70,7 @@ func TestStopAfterBrokenConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
w := config.Wrap("/dev/null", cfg)
|
w := config.Wrap("/dev/null", cfg)
|
||||||
|
|
||||||
srv := newAPIService(protocol.LocalDeviceID, w, "../../test/h1/https-cert.pem", "../../test/h1/https-key.pem", "", nil, nil, nil, nil, nil, nil)
|
srv := newAPIService(protocol.LocalDeviceID, w, "../../test/h1/https-cert.pem", "../../test/h1/https-key.pem", "", nil, nil, nil, nil, nil, nil, nil)
|
||||||
srv.started = make(chan string)
|
srv.started = make(chan string)
|
||||||
|
|
||||||
sup := suture.NewSimple("test")
|
sup := suture.NewSimple("test")
|
||||||
@ -469,6 +469,7 @@ func startHTTP(cfg *mockedConfig) (string, error) {
|
|||||||
httpsKeyFile := "../../test/h1/https-key.pem"
|
httpsKeyFile := "../../test/h1/https-key.pem"
|
||||||
assetDir := "../../gui"
|
assetDir := "../../gui"
|
||||||
eventSub := new(mockedEventSub)
|
eventSub := new(mockedEventSub)
|
||||||
|
diskEventSub := new(mockedEventSub)
|
||||||
discoverer := new(mockedCachingMux)
|
discoverer := new(mockedCachingMux)
|
||||||
connections := new(mockedConnections)
|
connections := new(mockedConnections)
|
||||||
errorLog := new(mockedLoggerRecorder)
|
errorLog := new(mockedLoggerRecorder)
|
||||||
@ -477,7 +478,7 @@ func startHTTP(cfg *mockedConfig) (string, error) {
|
|||||||
|
|
||||||
// Instantiate the API service
|
// Instantiate the API service
|
||||||
svc := newAPIService(protocol.LocalDeviceID, cfg, httpsCertFile, httpsKeyFile, assetDir, model,
|
svc := newAPIService(protocol.LocalDeviceID, cfg, httpsCertFile, httpsKeyFile, assetDir, model,
|
||||||
eventSub, discoverer, connections, errorLog, systemLog)
|
eventSub, diskEventSub, discoverer, connections, errorLog, systemLog)
|
||||||
svc.started = addrChan
|
svc.started = addrChan
|
||||||
|
|
||||||
// Actually start the API service
|
// Actually start the API service
|
||||||
|
|||||||
@ -552,6 +552,7 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
|
|||||||
// events. The LocalChangeDetected event might overwhelm the event
|
// events. The LocalChangeDetected event might overwhelm the event
|
||||||
// receiver in some situations so we will not subscribe to it here.
|
// receiver in some situations so we will not subscribe to it here.
|
||||||
apiSub := events.NewBufferedSubscription(events.Default.Subscribe(events.AllEvents&^events.LocalChangeDetected), 1000)
|
apiSub := events.NewBufferedSubscription(events.Default.Subscribe(events.AllEvents&^events.LocalChangeDetected), 1000)
|
||||||
|
diskSub := events.NewBufferedSubscription(events.Default.Subscribe(events.LocalChangeDetected), 1000)
|
||||||
|
|
||||||
if len(os.Getenv("GOMAXPROCS")) == 0 {
|
if len(os.Getenv("GOMAXPROCS")) == 0 {
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
@ -752,7 +753,7 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
|
|||||||
|
|
||||||
// GUI
|
// GUI
|
||||||
|
|
||||||
setupGUI(mainService, cfg, m, apiSub, cachedDiscovery, connectionsService, errors, systemLog, runtimeOptions)
|
setupGUI(mainService, cfg, m, apiSub, diskSub, cachedDiscovery, connectionsService, errors, systemLog, runtimeOptions)
|
||||||
|
|
||||||
if runtimeOptions.cpuProfile {
|
if runtimeOptions.cpuProfile {
|
||||||
f, err := os.Create(fmt.Sprintf("cpu-%d.pprof", os.Getpid()))
|
f, err := os.Create(fmt.Sprintf("cpu-%d.pprof", os.Getpid()))
|
||||||
@ -928,7 +929,7 @@ func startAuditing(mainService *suture.Supervisor) {
|
|||||||
l.Infoln("Audit log in", auditFile)
|
l.Infoln("Audit log in", auditFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupGUI(mainService *suture.Supervisor, cfg *config.Wrapper, m *model.Model, apiSub events.BufferedSubscription, discoverer discover.CachingMux, connectionsService *connections.Service, errors, systemLog logger.Recorder, runtimeOptions RuntimeOptions) {
|
func setupGUI(mainService *suture.Supervisor, cfg *config.Wrapper, m *model.Model, apiSub events.BufferedSubscription, diskSub events.BufferedSubscription, discoverer discover.CachingMux, connectionsService *connections.Service, errors, systemLog logger.Recorder, runtimeOptions RuntimeOptions) {
|
||||||
guiCfg := cfg.GUI()
|
guiCfg := cfg.GUI()
|
||||||
|
|
||||||
if !guiCfg.Enabled {
|
if !guiCfg.Enabled {
|
||||||
@ -939,7 +940,7 @@ func setupGUI(mainService *suture.Supervisor, cfg *config.Wrapper, m *model.Mode
|
|||||||
l.Warnln("Insecure admin access is enabled.")
|
l.Warnln("Insecure admin access is enabled.")
|
||||||
}
|
}
|
||||||
|
|
||||||
api := newAPIService(myID, cfg, locations[locHTTPSCertFile], locations[locHTTPSKeyFile], runtimeOptions.assetDir, m, apiSub, discoverer, connectionsService, errors, systemLog)
|
api := newAPIService(myID, cfg, locations[locHTTPSCertFile], locations[locHTTPSKeyFile], runtimeOptions.assetDir, m, apiSub, diskSub, discoverer, connectionsService, errors, systemLog)
|
||||||
cfg.Subscribe(api)
|
cfg.Subscribe(api)
|
||||||
mainService.Add(api)
|
mainService.Add(api)
|
||||||
|
|
||||||
|
|||||||
@ -1465,12 +1465,12 @@ func sendIndexTo(minSequence int64, conn protocol.Connection, folder string, fs
|
|||||||
func (m *Model) updateLocalsFromScanning(folder string, fs []protocol.FileInfo) {
|
func (m *Model) updateLocalsFromScanning(folder string, fs []protocol.FileInfo) {
|
||||||
m.updateLocals(folder, fs)
|
m.updateLocals(folder, fs)
|
||||||
|
|
||||||
// Fire the LocalChangeDetected event to notify listeners about local
|
|
||||||
// updates.
|
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
path := m.folderCfgs[folder].Path()
|
folderCfg := m.folderCfgs[folder]
|
||||||
m.fmut.RUnlock()
|
m.fmut.RUnlock()
|
||||||
m.localChangeDetected(folder, path, fs)
|
|
||||||
|
// Fire the LocalChangeDetected event to notify listeners about local updates.
|
||||||
|
m.localChangeDetected(folderCfg, fs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) updateLocalsFromPulling(folder string, fs []protocol.FileInfo) {
|
func (m *Model) updateLocalsFromPulling(folder string, fs []protocol.FileInfo) {
|
||||||
@ -1500,9 +1500,8 @@ func (m *Model) updateLocals(folder string, fs []protocol.FileInfo) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) localChangeDetected(folder, path string, files []protocol.FileInfo) {
|
func (m *Model) localChangeDetected(folderCfg config.FolderConfiguration, files []protocol.FileInfo) {
|
||||||
// For windows paths, strip unwanted chars from the front
|
path := strings.Replace(folderCfg.Path(), `\\?\`, "", 1)
|
||||||
path = strings.Replace(path, `\\?\`, "", 1)
|
|
||||||
|
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
objType := "file"
|
objType := "file"
|
||||||
@ -1526,11 +1525,13 @@ func (m *Model) localChangeDetected(folder, path string, files []protocol.FileIn
|
|||||||
action = "deleted"
|
action = "deleted"
|
||||||
}
|
}
|
||||||
|
|
||||||
// The full file path, adjusted to the local path separator character.
|
// The full file path, adjusted to the local path separator character. Also
|
||||||
|
// for windows paths, strip unwanted chars from the front.
|
||||||
path := filepath.Join(path, filepath.FromSlash(file.Name))
|
path := filepath.Join(path, filepath.FromSlash(file.Name))
|
||||||
|
|
||||||
events.Default.Log(events.LocalChangeDetected, map[string]string{
|
events.Default.Log(events.LocalChangeDetected, map[string]string{
|
||||||
"folder": folder,
|
"folderID": folderCfg.ID,
|
||||||
|
"label": folderCfg.Label,
|
||||||
"action": action,
|
"action": action,
|
||||||
"type": objType,
|
"type": objType,
|
||||||
"path": path,
|
"path": path,
|
||||||
|
|||||||
@ -27,6 +27,7 @@ var jsonEndpoints = []string{
|
|||||||
"/rest/db/status?folder=default",
|
"/rest/db/status?folder=default",
|
||||||
"/rest/db/browse?folder=default",
|
"/rest/db/browse?folder=default",
|
||||||
"/rest/events?since=-1&limit=5",
|
"/rest/events?since=-1&limit=5",
|
||||||
|
"/rest/events/disk?since=-1&limit=5",
|
||||||
"/rest/stats/device",
|
"/rest/stats/device",
|
||||||
"/rest/stats/folder",
|
"/rest/stats/folder",
|
||||||
"/rest/svc/deviceid?id=I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU",
|
"/rest/svc/deviceid?id=I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user