This commit is contained in:
@@ -291,6 +291,10 @@ func (s *service) Serve(ctx context.Context) error {
|
||||
restMux.HandlerFunc(http.MethodPost, "/rest/system/resume", s.makeDevicePauseHandler(false)) // [device]
|
||||
restMux.HandlerFunc(http.MethodPost, "/rest/system/debug", s.postSystemDebug) // [enable] [disable]
|
||||
|
||||
// The DELETE handlers
|
||||
restMux.HandlerFunc(http.MethodDelete, "/rest/cluster/pending/devices", s.deletePendingDevices) // device
|
||||
restMux.HandlerFunc(http.MethodDelete, "/rest/cluster/pending/folders", s.deletePendingFolders) // folder [device]
|
||||
|
||||
// Config endpoints
|
||||
|
||||
configBuilder := &configMuxBuilder{
|
||||
@@ -632,6 +636,21 @@ func (s *service) getPendingDevices(w http.ResponseWriter, r *http.Request) {
|
||||
sendJSON(w, devices)
|
||||
}
|
||||
|
||||
func (s *service) deletePendingDevices(w http.ResponseWriter, r *http.Request) {
|
||||
qs := r.URL.Query()
|
||||
|
||||
device := qs.Get("device")
|
||||
deviceID, err := protocol.DeviceIDFromString(device)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.model.DismissPendingDevice(deviceID); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *service) getPendingFolders(w http.ResponseWriter, r *http.Request) {
|
||||
qs := r.URL.Query()
|
||||
|
||||
@@ -650,6 +669,22 @@ func (s *service) getPendingFolders(w http.ResponseWriter, r *http.Request) {
|
||||
sendJSON(w, folders)
|
||||
}
|
||||
|
||||
func (s *service) deletePendingFolders(w http.ResponseWriter, r *http.Request) {
|
||||
qs := r.URL.Query()
|
||||
|
||||
device := qs.Get("device")
|
||||
deviceID, err := protocol.DeviceIDFromString(device)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
folderID := qs.Get("folder")
|
||||
|
||||
if err := s.model.DismissPendingFolder(deviceID, folderID); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *service) restPing(w http.ResponseWriter, r *http.Request) {
|
||||
sendJSON(w, map[string]string{"ping": "pong"})
|
||||
}
|
||||
|
||||
+11
-6
@@ -26,11 +26,13 @@ func (db *Lowlevel) AddOrUpdatePendingDevice(device protocol.DeviceID, name, add
|
||||
return db.Put(key, bs)
|
||||
}
|
||||
|
||||
func (db *Lowlevel) RemovePendingDevice(device protocol.DeviceID) {
|
||||
func (db *Lowlevel) RemovePendingDevice(device protocol.DeviceID) error {
|
||||
key := db.keyer.GeneratePendingDeviceKey(nil, device[:])
|
||||
if err := db.Delete(key); err != nil {
|
||||
l.Warnf("Failed to remove pending device entry: %v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PendingDevices enumerates all entries. Invalid ones are dropped from the database
|
||||
@@ -79,32 +81,35 @@ func (db *Lowlevel) AddOrUpdatePendingFolder(id string, of ObservedFolder, devic
|
||||
}
|
||||
|
||||
// RemovePendingFolderForDevice removes entries for specific folder / device combinations.
|
||||
func (db *Lowlevel) RemovePendingFolderForDevice(id string, device protocol.DeviceID) {
|
||||
func (db *Lowlevel) RemovePendingFolderForDevice(id string, device protocol.DeviceID) error {
|
||||
key, err := db.keyer.GeneratePendingFolderKey(nil, device[:], []byte(id))
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
if err := db.Delete(key); err != nil {
|
||||
l.Warnf("Failed to remove pending folder entry: %v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemovePendingFolder removes all entries matching a specific folder ID.
|
||||
func (db *Lowlevel) RemovePendingFolder(id string) {
|
||||
func (db *Lowlevel) RemovePendingFolder(id string) error {
|
||||
iter, err := db.NewPrefixIterator([]byte{KeyTypePendingFolder})
|
||||
if err != nil {
|
||||
l.Infof("Could not iterate through pending folder entries: %v", err)
|
||||
return
|
||||
return err
|
||||
}
|
||||
defer iter.Release()
|
||||
for iter.Next() {
|
||||
if id != string(db.keyer.FolderFromPendingFolderKey(iter.Key())) {
|
||||
continue
|
||||
}
|
||||
if err := db.Delete(iter.Key()); err != nil {
|
||||
if err = db.Delete(iter.Key()); err != nil {
|
||||
l.Warnf("Failed to remove pending folder entry: %v", err)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Consolidated information about a pending folder
|
||||
|
||||
@@ -177,6 +177,29 @@ type Model struct {
|
||||
result1 map[protocol.DeviceID]stats.DeviceStatistics
|
||||
result2 error
|
||||
}
|
||||
DismissPendingDeviceStub func(protocol.DeviceID) error
|
||||
dismissPendingDeviceMutex sync.RWMutex
|
||||
dismissPendingDeviceArgsForCall []struct {
|
||||
arg1 protocol.DeviceID
|
||||
}
|
||||
dismissPendingDeviceReturns struct {
|
||||
result1 error
|
||||
}
|
||||
dismissPendingDeviceReturnsOnCall map[int]struct {
|
||||
result1 error
|
||||
}
|
||||
DismissPendingFolderStub func(protocol.DeviceID, string) error
|
||||
dismissPendingFolderMutex sync.RWMutex
|
||||
dismissPendingFolderArgsForCall []struct {
|
||||
arg1 protocol.DeviceID
|
||||
arg2 string
|
||||
}
|
||||
dismissPendingFolderReturns struct {
|
||||
result1 error
|
||||
}
|
||||
dismissPendingFolderReturnsOnCall map[int]struct {
|
||||
result1 error
|
||||
}
|
||||
DownloadProgressStub func(protocol.DeviceID, string, []protocol.FileDownloadProgressUpdate) error
|
||||
downloadProgressMutex sync.RWMutex
|
||||
downloadProgressArgsForCall []struct {
|
||||
@@ -1332,6 +1355,129 @@ func (fake *Model) DeviceStatisticsReturnsOnCall(i int, result1 map[protocol.Dev
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *Model) DismissPendingDevice(arg1 protocol.DeviceID) error {
|
||||
fake.dismissPendingDeviceMutex.Lock()
|
||||
ret, specificReturn := fake.dismissPendingDeviceReturnsOnCall[len(fake.dismissPendingDeviceArgsForCall)]
|
||||
fake.dismissPendingDeviceArgsForCall = append(fake.dismissPendingDeviceArgsForCall, struct {
|
||||
arg1 protocol.DeviceID
|
||||
}{arg1})
|
||||
stub := fake.DismissPendingDeviceStub
|
||||
fakeReturns := fake.dismissPendingDeviceReturns
|
||||
fake.recordInvocation("DismissPendingDevice", []interface{}{arg1})
|
||||
fake.dismissPendingDeviceMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *Model) DismissPendingDeviceCallCount() int {
|
||||
fake.dismissPendingDeviceMutex.RLock()
|
||||
defer fake.dismissPendingDeviceMutex.RUnlock()
|
||||
return len(fake.dismissPendingDeviceArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *Model) DismissPendingDeviceCalls(stub func(protocol.DeviceID) error) {
|
||||
fake.dismissPendingDeviceMutex.Lock()
|
||||
defer fake.dismissPendingDeviceMutex.Unlock()
|
||||
fake.DismissPendingDeviceStub = stub
|
||||
}
|
||||
|
||||
func (fake *Model) DismissPendingDeviceArgsForCall(i int) protocol.DeviceID {
|
||||
fake.dismissPendingDeviceMutex.RLock()
|
||||
defer fake.dismissPendingDeviceMutex.RUnlock()
|
||||
argsForCall := fake.dismissPendingDeviceArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *Model) DismissPendingDeviceReturns(result1 error) {
|
||||
fake.dismissPendingDeviceMutex.Lock()
|
||||
defer fake.dismissPendingDeviceMutex.Unlock()
|
||||
fake.DismissPendingDeviceStub = nil
|
||||
fake.dismissPendingDeviceReturns = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *Model) DismissPendingDeviceReturnsOnCall(i int, result1 error) {
|
||||
fake.dismissPendingDeviceMutex.Lock()
|
||||
defer fake.dismissPendingDeviceMutex.Unlock()
|
||||
fake.DismissPendingDeviceStub = nil
|
||||
if fake.dismissPendingDeviceReturnsOnCall == nil {
|
||||
fake.dismissPendingDeviceReturnsOnCall = make(map[int]struct {
|
||||
result1 error
|
||||
})
|
||||
}
|
||||
fake.dismissPendingDeviceReturnsOnCall[i] = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *Model) DismissPendingFolder(arg1 protocol.DeviceID, arg2 string) error {
|
||||
fake.dismissPendingFolderMutex.Lock()
|
||||
ret, specificReturn := fake.dismissPendingFolderReturnsOnCall[len(fake.dismissPendingFolderArgsForCall)]
|
||||
fake.dismissPendingFolderArgsForCall = append(fake.dismissPendingFolderArgsForCall, struct {
|
||||
arg1 protocol.DeviceID
|
||||
arg2 string
|
||||
}{arg1, arg2})
|
||||
stub := fake.DismissPendingFolderStub
|
||||
fakeReturns := fake.dismissPendingFolderReturns
|
||||
fake.recordInvocation("DismissPendingFolder", []interface{}{arg1, arg2})
|
||||
fake.dismissPendingFolderMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1, arg2)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *Model) DismissPendingFolderCallCount() int {
|
||||
fake.dismissPendingFolderMutex.RLock()
|
||||
defer fake.dismissPendingFolderMutex.RUnlock()
|
||||
return len(fake.dismissPendingFolderArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *Model) DismissPendingFolderCalls(stub func(protocol.DeviceID, string) error) {
|
||||
fake.dismissPendingFolderMutex.Lock()
|
||||
defer fake.dismissPendingFolderMutex.Unlock()
|
||||
fake.DismissPendingFolderStub = stub
|
||||
}
|
||||
|
||||
func (fake *Model) DismissPendingFolderArgsForCall(i int) (protocol.DeviceID, string) {
|
||||
fake.dismissPendingFolderMutex.RLock()
|
||||
defer fake.dismissPendingFolderMutex.RUnlock()
|
||||
argsForCall := fake.dismissPendingFolderArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *Model) DismissPendingFolderReturns(result1 error) {
|
||||
fake.dismissPendingFolderMutex.Lock()
|
||||
defer fake.dismissPendingFolderMutex.Unlock()
|
||||
fake.DismissPendingFolderStub = nil
|
||||
fake.dismissPendingFolderReturns = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *Model) DismissPendingFolderReturnsOnCall(i int, result1 error) {
|
||||
fake.dismissPendingFolderMutex.Lock()
|
||||
defer fake.dismissPendingFolderMutex.Unlock()
|
||||
fake.DismissPendingFolderStub = nil
|
||||
if fake.dismissPendingFolderReturnsOnCall == nil {
|
||||
fake.dismissPendingFolderReturnsOnCall = make(map[int]struct {
|
||||
result1 error
|
||||
})
|
||||
}
|
||||
fake.dismissPendingFolderReturnsOnCall[i] = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *Model) DownloadProgress(arg1 protocol.DeviceID, arg2 string, arg3 []protocol.FileDownloadProgressUpdate) error {
|
||||
var arg3Copy []protocol.FileDownloadProgressUpdate
|
||||
if arg3 != nil {
|
||||
@@ -3254,6 +3400,10 @@ func (fake *Model) Invocations() map[string][][]interface{} {
|
||||
defer fake.delayScanMutex.RUnlock()
|
||||
fake.deviceStatisticsMutex.RLock()
|
||||
defer fake.deviceStatisticsMutex.RUnlock()
|
||||
fake.dismissPendingDeviceMutex.RLock()
|
||||
defer fake.dismissPendingDeviceMutex.RUnlock()
|
||||
fake.dismissPendingFolderMutex.RLock()
|
||||
defer fake.dismissPendingFolderMutex.RUnlock()
|
||||
fake.downloadProgressMutex.RLock()
|
||||
defer fake.downloadProgressMutex.RUnlock()
|
||||
fake.folderErrorsMutex.RLock()
|
||||
|
||||
+78
-15
@@ -107,6 +107,8 @@ type Model interface {
|
||||
|
||||
PendingDevices() (map[protocol.DeviceID]db.ObservedDevice, error)
|
||||
PendingFolders(device protocol.DeviceID) (map[string]db.PendingFolder, error)
|
||||
DismissPendingDevice(device protocol.DeviceID) error
|
||||
DismissPendingFolder(device protocol.DeviceID, folder string) error
|
||||
|
||||
StartDeadlockDetector(timeout time.Duration)
|
||||
GlobalDirectoryTree(folder, prefix string, levels int, dirsOnly bool) ([]*TreeEntry, error)
|
||||
@@ -1374,17 +1376,18 @@ func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.Devi
|
||||
}
|
||||
|
||||
indexHandlers.RemoveAllExcept(seenFolders)
|
||||
expiredPendingList := make([]map[string]string, 0, len(expiredPending))
|
||||
for folder := range expiredPending {
|
||||
m.db.RemovePendingFolderForDevice(folder, deviceID)
|
||||
}
|
||||
if len(updatedPending) > 0 || len(expiredPending) > 0 {
|
||||
expiredPendingList := make([]map[string]string, 0, len(expiredPending))
|
||||
for folderID := range expiredPending {
|
||||
expiredPendingList = append(expiredPendingList, map[string]string{
|
||||
"folderID": folderID,
|
||||
"deviceID": deviceID.String(),
|
||||
})
|
||||
if err = m.db.RemovePendingFolderForDevice(folder, deviceID); err != nil {
|
||||
// Nothing we can fix; logged from DB already
|
||||
continue
|
||||
}
|
||||
expiredPendingList = append(expiredPendingList, map[string]string{
|
||||
"folderID": folder,
|
||||
"deviceID": deviceID.String(),
|
||||
})
|
||||
}
|
||||
if len(updatedPending) > 0 || len(expiredPendingList) > 0 {
|
||||
m.evLogger.Log(events.PendingFoldersChanged, map[string]interface{}{
|
||||
"added": updatedPending,
|
||||
"removed": expiredPendingList,
|
||||
@@ -2947,10 +2950,13 @@ func (m *model) cleanPending(existingDevices map[protocol.DeviceID]config.Device
|
||||
// folders as well, assuming the folder is no longer of interest
|
||||
// at all (but might become pending again).
|
||||
l.Debugf("Discarding pending removed folder %v from all devices", folderID)
|
||||
m.db.RemovePendingFolder(folderID)
|
||||
removedPendingFolders = append(removedPendingFolders, map[string]string{
|
||||
"folderID": folderID,
|
||||
})
|
||||
if err := m.db.RemovePendingFolder(folderID); err != nil {
|
||||
// Nothing we can fix; logged from DB already
|
||||
} else {
|
||||
removedPendingFolders = append(removedPendingFolders, map[string]string{
|
||||
"folderID": folderID,
|
||||
})
|
||||
}
|
||||
continue
|
||||
}
|
||||
for deviceID := range pf.OfferedBy {
|
||||
@@ -2969,7 +2975,10 @@ func (m *model) cleanPending(existingDevices map[protocol.DeviceID]config.Device
|
||||
}
|
||||
continue
|
||||
removeFolderForDevice:
|
||||
m.db.RemovePendingFolderForDevice(folderID, deviceID)
|
||||
if err := m.db.RemovePendingFolderForDevice(folderID, deviceID); err != nil {
|
||||
// Nothing we can fix; logged from DB already
|
||||
continue
|
||||
}
|
||||
removedPendingFolders = append(removedPendingFolders, map[string]string{
|
||||
"folderID": folderID,
|
||||
"deviceID": deviceID.String(),
|
||||
@@ -2999,7 +3008,10 @@ func (m *model) cleanPending(existingDevices map[protocol.DeviceID]config.Device
|
||||
}
|
||||
continue
|
||||
removeDevice:
|
||||
m.db.RemovePendingDevice(deviceID)
|
||||
if err := m.db.RemovePendingDevice(deviceID); err != nil {
|
||||
// Nothing we can fix; logged from DB already
|
||||
continue
|
||||
}
|
||||
removedPendingDevices = append(removedPendingDevices, map[string]string{
|
||||
"deviceID": deviceID.String(),
|
||||
})
|
||||
@@ -3041,6 +3053,57 @@ func (m *model) PendingFolders(device protocol.DeviceID) (map[string]db.PendingF
|
||||
return m.db.PendingFoldersForDevice(device)
|
||||
}
|
||||
|
||||
// DismissPendingDevices removes the record of a specific pending device.
|
||||
func (m *model) DismissPendingDevice(device protocol.DeviceID) error {
|
||||
l.Debugf("Discarding pending device %v", device)
|
||||
err := m.db.RemovePendingDevice(device)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
removedPendingDevices := []map[string]string{
|
||||
{"deviceID": device.String()},
|
||||
}
|
||||
m.evLogger.Log(events.PendingDevicesChanged, map[string]interface{}{
|
||||
"removed": removedPendingDevices,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// DismissPendingFolders removes records of pending folders. Either a specific folder /
|
||||
// device combination, or all matching a specific folder ID if the device argument is
|
||||
// specified as EmptyDeviceID.
|
||||
func (m *model) DismissPendingFolder(device protocol.DeviceID, folder string) error {
|
||||
var removedPendingFolders []map[string]string
|
||||
if device == protocol.EmptyDeviceID {
|
||||
l.Debugf("Discarding pending removed folder %s from all devices", folder)
|
||||
err := m.db.RemovePendingFolder(folder)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
removedPendingFolders = []map[string]string{
|
||||
{"folderID": folder},
|
||||
}
|
||||
} else {
|
||||
l.Debugf("Discarding pending folder %s from device %v", folder, device)
|
||||
err := m.db.RemovePendingFolderForDevice(folder, device)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
removedPendingFolders = []map[string]string{
|
||||
{
|
||||
"folderID": folder,
|
||||
"deviceID": device.String(),
|
||||
},
|
||||
}
|
||||
}
|
||||
if len(removedPendingFolders) > 0 {
|
||||
m.evLogger.Log(events.PendingFoldersChanged, map[string]interface{}{
|
||||
"removed": removedPendingFolders,
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// mapFolders returns a map of folder ID to folder configuration for the given
|
||||
// slice of folder configurations.
|
||||
func mapFolders(folders []config.FolderConfiguration) map[string]config.FolderConfiguration {
|
||||
|
||||
Reference in New Issue
Block a user