Feature: Offline device history with delete and remote uninstall
Persist every device that has ever logged in to the Go server in a small JSON store (devices.json, overridable with YAMA_DEVICES_FILE and capped by YAMA_DEVICES_MAX, default 1000 newest). The store follows the users.json convention: atomic temp-file write with fsync and rename, a 500 ms debounce for bursts of reconnects, first_seen preserved across logins, and pruning that evicts offline records before devices that are still connected. Devices connected at shutdown are stamped offline before the final flush so last_seen stays accurate across restarts. The server advertises an offlineDevices capability and device_list now appends historical devices that are not online, marked with online:false and last_seen. The web UI shows an Offline checkbox after Total/Showing (state kept in localStorage), renders offline cards dimmed and dashed, and always lists them in the screen-wall sidebar. Older servers without the capability keep the online-only list. Grid and List cards gain a delete button after the terminal button, admin only, behind a confirmation dialog. Deleting an offline device removes its history record; deleting an online device first sends the single-byte TOKEN_UNINSTALL command (matching the MFC OnOnlineUninstall flow) and then removes the record too, so the device does not come back as a dead offline entry. The agent's own disconnect then runs the existing cleanup path: screen and terminal sub-connections are closed, the hub entry is removed, and browsers receive device_offline to release their watch sets, wall sessions, and stream references. Wall mode exposes no delete action. Tests cover history persistence/pruning/recovery, offline ordering, the device_list merge, and all three delete outcomes (offline removal, online uninstall with a single 0x3F byte, and viewer rejection). Co-Authored-By: deepseek-flash
This commit is contained in:
1 parent
1922f87dd2
commit
b1ddec1b8a
12 files changed
+1212
-27
No files matched your search
@@ -0,0 +1,53 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/yuanyuanxiang/SimpleRemoter/server/go/connection"
|
||||
"github.com/yuanyuanxiang/SimpleRemoter/server/go/devicehistory"
|
||||
"github.com/yuanyuanxiang/SimpleRemoter/server/go/hub"
|
||||
)
|
||||
|
||||
func TestListDevicesWithOffline(t *testing.T) {
|
||||
h := hub.New()
|
||||
hist, err := devicehistory.Open(filepath.Join(t.TempDir(), "devices.json"), 100, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Open history: %v", err)
|
||||
}
|
||||
t.Cleanup(func() { _ = hist.Close() })
|
||||
h.Subscribe(hist)
|
||||
|
||||
h.Register(&hub.Device{ID: "online", Name: "Online"},
|
||||
&connection.Context{RemoteAddr: "10.0.0.1:1000"})
|
||||
h.Register(&hub.Device{ID: "gone", Name: "Gone"},
|
||||
&connection.Context{RemoteAddr: "10.0.0.2:1000"})
|
||||
h.Unregister("gone")
|
||||
|
||||
wh := &wsHub{devices: h, history: hist}
|
||||
got := wh.listDevicesWithOffline()
|
||||
if len(got) != 2 {
|
||||
t.Fatalf("devices = %d, want 2 (1 online + 1 offline)", len(got))
|
||||
}
|
||||
if got[0].ID != "online" || !got[0].Online {
|
||||
t.Fatalf("first = %+v, want online device", got[0])
|
||||
}
|
||||
if got[1].ID != "gone" || got[1].Online {
|
||||
t.Fatalf("second = %+v, want offline device", got[1])
|
||||
}
|
||||
if got[1].LastSeen == 0 {
|
||||
t.Fatal("offline device missing last_seen")
|
||||
}
|
||||
}
|
||||
|
||||
func TestListDevicesWithoutHistoryIsOnlineOnly(t *testing.T) {
|
||||
h := hub.New()
|
||||
h.Register(&hub.Device{ID: "online", Name: "Online"},
|
||||
&connection.Context{RemoteAddr: "10.0.0.1:1000"})
|
||||
|
||||
wh := &wsHub{devices: h}
|
||||
got := wh.listDevicesWithOffline()
|
||||
if len(got) != 1 || got[0].ID != "online" {
|
||||
t.Fatalf("devices = %+v, want just the online device", got)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user