Refresh stale device capability timestamps

This commit is contained in:
kris
2026-04-06 14:38:18 +08:00
parent 5789707072
commit 5782804df3
2 changed files with 106 additions and 15 deletions

View File

@@ -1742,6 +1742,36 @@ function normalizeDeviceCapabilities(
};
}
function refreshDeviceCapabilityLastSeenAt(
raw: DeviceCapabilitiesInput | undefined,
fallbackLastSeenAt: string,
preserveExistingLastSeenAt = true,
): DeviceCapabilitiesInput | undefined {
if (!raw) {
return raw;
}
return {
gui: raw.gui
? {
...raw.gui,
lastSeenAt:
preserveExistingLastSeenAt
? trimToDefined(raw.gui.lastSeenAt) ?? fallbackLastSeenAt
: fallbackLastSeenAt,
}
: raw.gui,
cli: raw.cli
? {
...raw.cli,
lastSeenAt:
preserveExistingLastSeenAt
? trimToDefined(raw.cli.lastSeenAt) ?? fallbackLastSeenAt
: fallbackLastSeenAt,
}
: raw.cli,
};
}
function normalizePreferredExecutionMode(value: unknown): DeviceExecutionMode {
return value === "gui" ? "gui" : "cli";
}
@@ -7273,6 +7303,7 @@ export async function updateDevice(deviceId: string, payload: DeviceUpdatePayloa
const device = await mutateState((state) => {
const nextDevice = state.devices.find((item) => item.id === deviceId);
if (!nextDevice) throw new Error("DEVICE_NOT_FOUND");
const nextLastSeenAt = nowIso();
if (payload.name) nextDevice.name = payload.name.trim();
if (payload.avatar) nextDevice.avatar = payload.avatar.trim().slice(0, 2) || nextDevice.avatar;
@@ -7283,21 +7314,23 @@ export async function updateDevice(deviceId: string, payload: DeviceUpdatePayloa
if (payload.projects) {
nextDevice.projects = payload.projects.filter(Boolean);
}
if (payload.capabilities) {
nextDevice.capabilities = normalizeDeviceCapabilities(
{
gui: payload.capabilities.gui ?? nextDevice.capabilities?.gui,
cli: payload.capabilities.cli ?? nextDevice.capabilities?.cli,
},
trimToDefined(payload.capabilities.gui?.lastSeenAt) ??
trimToDefined(payload.capabilities.cli?.lastSeenAt) ??
nextDevice.lastSeenAt,
);
}
nextDevice.capabilities = normalizeDeviceCapabilities(
refreshDeviceCapabilityLastSeenAt(
payload.capabilities
? {
gui: payload.capabilities.gui ?? nextDevice.capabilities?.gui,
cli: payload.capabilities.cli ?? nextDevice.capabilities?.cli,
}
: nextDevice.capabilities,
nextLastSeenAt,
Boolean(payload.capabilities),
),
nextLastSeenAt,
);
if (payload.preferredExecutionMode !== undefined) {
nextDevice.preferredExecutionMode = normalizePreferredExecutionMode(payload.preferredExecutionMode);
}
nextDevice.lastSeenAt = nowIso();
nextDevice.lastSeenAt = nextLastSeenAt;
return nextDevice;
});
publishBossEvent("devices.updated", { deviceId });
@@ -7635,6 +7668,7 @@ export async function upsertDeviceHeartbeat(payload: {
if (device.token && payload.token && device.token !== payload.token && !claimedEnrollment) {
throw new Error("DEVICE_TOKEN_MISMATCH");
}
const nextLastSeenAt = nowIso();
device.name = payload.name;
device.avatar = payload.avatar;
device.account = payload.account;
@@ -7643,12 +7677,16 @@ export async function upsertDeviceHeartbeat(payload: {
device.projects = payload.projects;
device.quota5h = payload.quota5h;
device.quota7d = payload.quota7d;
device.lastSeenAt = nowIso();
device.lastSeenAt = nextLastSeenAt;
device.endpoint = payload.endpoint ?? device.endpoint;
device.token = claimedEnrollment?.token ?? payload.token ?? device.token;
device.capabilities = normalizeDeviceCapabilities(
payload.capabilities ?? device.capabilities,
device.lastSeenAt,
refreshDeviceCapabilityLastSeenAt(
payload.capabilities ?? device.capabilities,
nextLastSeenAt,
Boolean(payload.capabilities),
),
nextLastSeenAt,
);
if (device.preferredExecutionMode === undefined && payload.preferredExecutionMode !== undefined) {
device.preferredExecutionMode = normalizePreferredExecutionMode(payload.preferredExecutionMode);