Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions src/components/devices/CommandLauncher.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createElement, useMemo, useState } from 'react';
import { Camera, FileText, Loader2, MoreHorizontal, Power, RefreshCcw, RotateCw, ServerCog } from 'lucide-react';
import { Camera, Download, FileText, Loader2, MoreHorizontal, Power, RefreshCcw, RotateCw, ServerCog } from 'lucide-react';
import DeviceService from '../../services/DeviceService';
import { useAuth } from '../../context/AuthContext';
import { COMMAND_PERMISSIONS, COMMAND_RISK, PERMISSIONS } from '../../utils/permissions';
Expand All @@ -9,9 +9,25 @@ const QUICK_COMMANDS = [
{ kind: 'config.refresh', label: 'Refresh Config', icon: RefreshCcw, confirm: false },
{ kind: 'chrome.screenshot', label: 'Screenshot', icon: Camera, confirm: false },
{ kind: 'kiosk.restart', label: 'Restart Kiosk', icon: ServerCog, confirm: true },
{ kind: 'system.reboot', label: 'Reboot Device', icon: Power, confirm: true }
{ kind: 'system.reboot', label: 'Reboot Device', icon: Power, confirm: true },
{
kind: 'update.install_now',
label: 'Update Now',
icon: Download,
confirm: 'Check for a new board app or agent and install it now? The board shows its update screen while it installs, instead of waiting for tonight.'
}
];

/**
* How long the agent may run a command. Most finish in seconds; an update has
* to download a release first (the install itself always finishes once it
* starts, whatever the deadline).
*/
const DEADLINE_MS = {
'logs.tail': 45000,
'update.install_now': 600000
};

/**
* The three command permissions are separately grantable, so the buttons have to
* carry their risk class on their face. `inspect` in particular is a surveillance
Expand Down Expand Up @@ -55,15 +71,16 @@ function CommandLauncher({ deviceId, onIssued, disabled }) {
]);

const issue = async (kind, payload = {}, confirmCommand = false) => {
if (confirmCommand && !confirm(`Issue ${kind} to this device?`)) return;
const question = typeof confirmCommand === 'string' ? confirmCommand : `Issue ${kind} to this device?`;
if (confirmCommand && !confirm(question)) return;

try {
setError('');
setSubmitting(kind);
const issued = await DeviceService.issueCommand(deviceId, {
kind,
payload,
deadlineMs: kind === 'logs.tail' ? 45000 : 30000
deadlineMs: DEADLINE_MS[kind] ?? 30000
});
onIssued?.(issued);
setShowLogs(false);
Expand Down
22 changes: 22 additions & 0 deletions src/components/devices/CommandOutput.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,28 @@ function CommandOutput({ command }) {
);
}

// { checkError?, results: [{ file, type, version, action, message }] }
if (command.kind === 'update.install_now' && Array.isArray(output.results)) {
return (
<div className="space-y-2 text-sm">
{output.checkError && (
<div className="rounded-lg bg-amber-50 p-3 text-amber-800">Could not check for updates: {output.checkError}</div>
)}
{output.results.length === 0 ? (
<div className="text-gray-600">Nothing to install: the board app and agent are up to date.</div>
) : (
<ul className="space-y-1">
{output.results.map((result, index) => (
<li key={`${result.file}-${index}`} className={result.action === 'rejected' ? 'text-red-700' : 'text-gray-700'}>
<span className="font-medium">{result.action}</span>: {result.message}
</li>
))}
</ul>
)}
</div>
);
}

return (
<pre className="max-h-[360px] overflow-auto rounded-lg bg-gray-50 p-4 text-xs text-gray-700">
{JSON.stringify(output, null, 2)}
Expand Down
1 change: 1 addition & 0 deletions src/utils/permissions.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export const COMMAND_PERMISSIONS = {
'config.refresh': PERMISSIONS.BOARD_COMMAND_BENIGN,
'kiosk.restart': PERMISSIONS.BOARD_COMMAND_DISRUPTIVE,
'system.reboot': PERMISSIONS.BOARD_COMMAND_DISRUPTIVE,
'update.install_now': PERMISSIONS.BOARD_COMMAND_DISRUPTIVE,
'chrome.screenshot': PERMISSIONS.BOARD_COMMAND_INSPECT,
'logs.tail': PERMISSIONS.BOARD_COMMAND_INSPECT,
};
Expand Down
Loading