mirror of
https://github.com/kunkundi/crossdesk-server.git
synced 2026-10-26 01:34:33 +08:00
[fix] show remote control peers in admin details
This commit is contained in:
1 parent
48918df647
commit
37d29b2359
5 files changed
+87
-3
No files matched your search
@@ -180,6 +180,7 @@ const char kAdminHtml[] = R"HTML(<!doctype html>
|
||||
.detail-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 10px 16px; }
|
||||
.detail-grid span { color: #667085; display: block; font-size: 12px; }
|
||||
.detail-grid strong { display: block; font-size: 14px; margin-top: 2px; }
|
||||
.peer-list { word-break: break-all; }
|
||||
.sort-order { min-width: 62px; }
|
||||
.pager { display: flex; gap: 8px; align-items: center; justify-content: flex-end; margin-top: 12px; color: #667085; font-size: 13px; flex-wrap: wrap; }
|
||||
.status { color: #067647; font-weight: 600; }
|
||||
@@ -417,14 +418,20 @@ const char kAdminHtml[] = R"HTML(<!doctype html>
|
||||
}
|
||||
|
||||
function sessionSummary(device) {
|
||||
const controlling = Number(device.active_control_count) || 0;
|
||||
const controlled = Number(device.active_controlled_count) || 0;
|
||||
const targets = Array.isArray(device.active_control_targets) ? device.active_control_targets : [];
|
||||
const controlledBy = Array.isArray(device.active_controlled_by) ? device.active_controlled_by : [];
|
||||
const controlling = Number(device.active_control_count) || targets.length;
|
||||
const controlled = Number(device.active_controlled_count) || controlledBy.length;
|
||||
const parts = [];
|
||||
if (controlling > 0) parts.push(`controlling ${controlling}`);
|
||||
if (controlled > 0) parts.push(`controlled ${controlled}`);
|
||||
if (controlled > 0) parts.push(`controlled by ${controlled}`);
|
||||
return parts.join(', ') || '-';
|
||||
}
|
||||
|
||||
function peerList(value) {
|
||||
return Array.isArray(value) && value.length ? value.join(', ') : '-';
|
||||
}
|
||||
|
||||
function appendDetailItem(parent, label, value, className, dataset) {
|
||||
const item = document.createElement('div');
|
||||
appendText(item, 'span', label);
|
||||
@@ -498,6 +505,8 @@ const char kAdminHtml[] = R"HTML(<!doctype html>
|
||||
appendDetailItem(details, 'Online since', formatTime(device.online_since));
|
||||
appendDetailItem(details, 'Last online', formatTime(device.online ? 0 : device.updated_at));
|
||||
appendDetailItem(details, 'Active session', sessionSummary(device));
|
||||
appendDetailItem(details, 'Controlling', peerList(device.active_control_targets), 'peer-list');
|
||||
appendDetailItem(details, 'Controlled by', peerList(device.active_controlled_by), 'peer-list');
|
||||
detailsCell.appendChild(details);
|
||||
detailsRow.appendChild(detailsCell);
|
||||
body.appendChild(detailsRow);
|
||||
@@ -1006,6 +1015,10 @@ AdminHttpResponse AdminController::HandleOverview(
|
||||
{"active_control_count", active_control_count},
|
||||
{"active_controlled_count",
|
||||
active_controlled_count},
|
||||
{"active_control_targets",
|
||||
device.active_control_targets},
|
||||
{"active_controlled_by",
|
||||
device.active_controlled_by},
|
||||
{"active_session_count",
|
||||
active_control_count +
|
||||
active_controlled_count}});
|
||||
|
||||
@@ -51,6 +51,27 @@ std::string NormalizeRemoteDeviceId(const std::string& device_id) {
|
||||
return device_id.rfind("C-", 0) == 0 ? device_id.substr(2) : device_id;
|
||||
}
|
||||
|
||||
std::vector<std::string> SplitCommaSeparatedIds(const std::string& value) {
|
||||
std::vector<std::string> result;
|
||||
size_t start = 0;
|
||||
while (start <= value.size()) {
|
||||
size_t end = value.find(',', start);
|
||||
std::string item =
|
||||
value.substr(start, end == std::string::npos ? std::string::npos
|
||||
: end - start);
|
||||
if (!item.empty()) {
|
||||
result.push_back(item);
|
||||
}
|
||||
if (end == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
start = end + 1;
|
||||
}
|
||||
std::sort(result.begin(), result.end());
|
||||
result.erase(std::unique(result.begin(), result.end()), result.end());
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string NormalizedRemoteDeviceExpr(const std::string& column) {
|
||||
return "CASE WHEN " + column + " LIKE 'C-%' THEN substr(" + column +
|
||||
", 3) ELSE " + column + " END";
|
||||
@@ -1271,6 +1292,22 @@ std::vector<OnlineDeviceInfo> DeviceDBManager::ListDevicePresence(
|
||||
" = device_presence.device_id), 0) "
|
||||
"AS active_controlled_count, "
|
||||
"COALESCE(("
|
||||
"SELECT GROUP_CONCAT(DISTINCT " +
|
||||
NormalizedRemoteDeviceExpr("host_id") +
|
||||
") FROM remote_control_sessions "
|
||||
"WHERE " +
|
||||
NormalizedRemoteDeviceExpr("guest_id") +
|
||||
" = device_presence.device_id), '') "
|
||||
"AS active_control_targets, "
|
||||
"COALESCE(("
|
||||
"SELECT GROUP_CONCAT(DISTINCT " +
|
||||
NormalizedRemoteDeviceExpr("guest_id") +
|
||||
") FROM remote_control_sessions "
|
||||
"WHERE " +
|
||||
NormalizedRemoteDeviceExpr("host_id") +
|
||||
" = device_presence.device_id), '') "
|
||||
"AS active_controlled_by, "
|
||||
"COALESCE(("
|
||||
"SELECT COUNT(*) FROM remote_control_sessions "
|
||||
"WHERE " +
|
||||
NormalizedRemoteDeviceExpr("guest_id") +
|
||||
@@ -1314,6 +1351,8 @@ std::vector<OnlineDeviceInfo> DeviceDBManager::ListDevicePresence(
|
||||
info.total_controlled_seconds = sqlite3_column_int64(stmt, 7);
|
||||
info.active_control_count = sqlite3_column_int64(stmt, 8);
|
||||
info.active_controlled_count = sqlite3_column_int64(stmt, 9);
|
||||
info.active_control_targets = SplitCommaSeparatedIds(ColumnText(stmt, 10));
|
||||
info.active_controlled_by = SplitCommaSeparatedIds(ColumnText(stmt, 11));
|
||||
result.push_back(info);
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
@@ -32,6 +32,8 @@ struct OnlineDeviceInfo {
|
||||
int64_t total_controlled_seconds = 0;
|
||||
int64_t active_control_count = 0;
|
||||
int64_t active_controlled_count = 0;
|
||||
std::vector<std::string> active_control_targets;
|
||||
std::vector<std::string> active_controlled_by;
|
||||
};
|
||||
|
||||
struct OnlineDurationStats {
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#include "admin_controller.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "admin_auth.h"
|
||||
#include "device_db_manager.h"
|
||||
@@ -19,6 +21,10 @@ int main() {
|
||||
++failures;
|
||||
}
|
||||
};
|
||||
auto contains_id = [](const std::vector<std::string>& ids,
|
||||
const std::string& id) {
|
||||
return std::find(ids.begin(), ids.end(), id) != ids.end();
|
||||
};
|
||||
|
||||
expect(AdminController::IsAdminRoute("/admin"), "/admin is admin route");
|
||||
expect(AdminController::IsAdminRoute("/api/admin/overview"),
|
||||
@@ -195,9 +201,19 @@ int main() {
|
||||
"overview applies device sort order");
|
||||
expect(active_body["devices"][0]["active_controlled_count"] == 2,
|
||||
"overview reports active controlled count");
|
||||
auto first_controlled_by =
|
||||
active_body["devices"][0]["active_controlled_by"]
|
||||
.get<std::vector<std::string>>();
|
||||
expect(contains_id(first_controlled_by, "device-admin-offline") &&
|
||||
contains_id(first_controlled_by, "device-admin-control"),
|
||||
"overview reports active controlled-by peer ids");
|
||||
expect(active_body["devices"][1]["id"] == "device-admin-control" &&
|
||||
active_body["devices"][1]["active_control_count"] == 1,
|
||||
"overview maps clone guest control count to base device");
|
||||
auto clone_targets = active_body["devices"][1]["active_control_targets"]
|
||||
.get<std::vector<std::string>>();
|
||||
expect(contains_id(clone_targets, "device-admin-1"),
|
||||
"overview maps clone guest control target to base device");
|
||||
expect(active_body["devices"][2]["active_control_count"] == 1,
|
||||
"overview reports active control count");
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#include "presence_manager.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "device_db_manager.h"
|
||||
|
||||
@@ -19,6 +21,10 @@ int main() {
|
||||
++failures;
|
||||
}
|
||||
};
|
||||
auto contains_id = [](const std::vector<std::string>& ids,
|
||||
const std::string& id) {
|
||||
return std::find(ids.begin(), ids.end(), id) != ids.end();
|
||||
};
|
||||
|
||||
expect(presence.GetOnlineDeviceCount() == 0,
|
||||
"initial online device count is zero");
|
||||
@@ -150,9 +156,13 @@ int main() {
|
||||
expect(active_devices[0].device_id == "device-1" &&
|
||||
active_devices[0].active_control_count == 1,
|
||||
"presence list reports active control count");
|
||||
expect(contains_id(active_devices[0].active_control_targets, "device-2"),
|
||||
"presence list reports active control target id");
|
||||
expect(active_devices[1].device_id == "device-2" &&
|
||||
active_devices[1].active_controlled_count == 1,
|
||||
"presence list reports active controlled count");
|
||||
expect(contains_id(active_devices[1].active_controlled_by, "device-1"),
|
||||
"presence list reports active controlled-by peer id");
|
||||
db.SetDeviceOnline("device-4", true);
|
||||
expect(db.StartRemoteControlSession("tx-clone", "device-2", "C-device-4"),
|
||||
"clone remote control session starts");
|
||||
@@ -161,6 +171,10 @@ int main() {
|
||||
expect(clone_control.size() == 1 &&
|
||||
clone_control[0].active_control_count == 1,
|
||||
"presence list maps clone guest control count to base device");
|
||||
expect(!clone_control.empty() &&
|
||||
contains_id(clone_control[0].active_control_targets,
|
||||
"device-2"),
|
||||
"presence list maps clone guest control target to base device");
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
expect(db.EndRemoteControlSession("tx-clone", "device-2", "C-device-4"),
|
||||
"clone remote control session ends");
|
||||
|
||||
Reference in new issue
Block a user