server restart thingy yay

This commit is contained in:
legop3
2026-09-14 18:42:42 -04:00
parent c56a8b1000
commit edc1b825f2
26 changed files with 330 additions and 312 deletions
+1
View File
@@ -25,6 +25,7 @@ export const restoreConfigurationRevision = (socket, payload) => emitAdminReques
export const createAdministrator = (socket, payload) => emitAdminRequest(socket, 'adminConfig:createAdministrator', payload);
export const updateAdministrator = (socket, payload) => emitAdminRequest(socket, 'adminConfig:updateAdministrator', payload);
export const deleteAdministrator = (socket, id) => emitAdminRequest(socket, 'adminConfig:deleteAdministrator', { id });
export const restartApplication = (socket) => emitAdminRequest(socket, 'server:restartApplication');
export const getSetupStatus = (socket) => emitAdminRequest(socket, 'setup:status');
export const createFirstAdministrator = (socket, payload) => emitAdminRequest(socket, 'setup:createAdministrator', payload);
+29 -1
View File
@@ -1,9 +1,10 @@
// Administration Overview
// Purpose: Summarizes configuration state, revision history, audit history, and links to existing health/report surfaces.
// Scope: Presents persisted administration metadata without duplicating operational service implementations.
import { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import CardFrame from '../../components/CardFrame/index.jsx';
import { restoreConfigurationRevision } from '../api.js';
import { restartApplication, restoreConfigurationRevision } from '../api.js';
function formatDate(value) {
return Number.isFinite(Number(value)) ? new Date(Number(value)).toLocaleString() : 'unknown';
@@ -11,6 +12,16 @@ function formatDate(value) {
export default function AdminOverview({ snapshot, socket, runSensitive, onSnapshot }) {
const config = snapshot.configuration;
const [restartRequested, setRestartRequested] = useState(false);
useEffect(() => {
if (!restartRequested) return undefined;
// The socket reconnect is the simplest authoritative success signal: the
// replacement process is accepting browser connections again.
const handleReconnect = () => setRestartRequested(false);
socket.once('connect', handleReconnect);
return () => socket.off('connect', handleReconnect);
}, [restartRequested, socket]);
async function restore(revision) {
if (!window.confirm(`Restore configuration revision ${revision}? This creates and immediately applies a new active revision.`)) return;
@@ -25,6 +36,17 @@ export default function AdminOverview({ snapshot, socket, runSensitive, onSnapsh
}
}
async function restart() {
if (!window.confirm('Restart the MultiRover application now? Connected rovers and browsers will disconnect briefly; the server host will not reboot.')) return;
setRestartRequested(true);
try {
await runSensitive(() => restartApplication(socket));
} catch (error) {
setRestartRequested(false);
window.alert(error.message);
}
}
return (
<div className="space-y-0.5">
<CardFrame title="Administration overview" meta={`revision ${config.revision}`} bodyClassName="grid gap-0.5 p-0.5 md:grid-cols-3">
@@ -36,6 +58,12 @@ export default function AdminOverview({ snapshot, socket, runSensitive, onSnapsh
<Link className="button-dark" to="/reports">Open fleet reports</Link>
<Link className="button-dark" to="/">Open driver application</Link>
</CardFrame>
<CardFrame title="Application" bodyClassName="space-y-0.5 p-1 text-sm">
<p className="text-slate-300">Restart only the MultiRover application. The process supervisor starts it again automatically without rebooting the host.</p>
<button type="button" className="button-danger" disabled={restartRequested} onClick={restart}>
{restartRequested ? 'Waiting for application…' : 'Restart application'}
</button>
</CardFrame>
<CardFrame title="Configuration revisions" meta={snapshot.revisions.length} bodyClassName="max-h-64 overflow-y-auto p-0.5 text-xs">
{snapshot.revisions.map((revision) => (
<div key={revision.revision} className="surface mb-0.5 grid items-center gap-0.5 p-0.5 md:grid-cols-[5rem_1fr_1fr_1fr_auto]">
@@ -105,7 +105,6 @@ export default function AdminPanelContent() {
rebootRover,
updateRover,
updateAllRovers,
rebootServer,
setAudioLevels,
setPersonalAudioAdjustmentRange,
setPrivateSafety,
@@ -119,7 +118,6 @@ export default function AdminPanelContent() {
const [lockStates, setLockStates] = useState({});
const [rebootStates, setRebootStates] = useState({});
const [updateStates, setUpdateStates] = useState({});
const [serverRebooting, setServerRebooting] = useState(false);
const [clearingLlmHistory, setClearingLlmHistory] = useState(false);
const [clearingOverseerHistory, setClearingOverseerHistory] = useState(false);
const health = session?.health || null;
@@ -229,18 +227,6 @@ export default function AdminPanelContent() {
}
};
const handleServerReboot = async () => {
const ok = window.confirm('Reboot the server host now? This will disconnect all users.');
if (!ok) return;
setServerRebooting(true);
try {
await rebootServer();
} catch (err) {
alert(err.message);
setServerRebooting(false);
}
};
const handleClearLlmHistory = async () => {
const ok = window.confirm(
'Clear LLM commentary history now? This resets chat context, bot memory, and rover activity metrics for narration.',
@@ -421,14 +407,6 @@ export default function AdminPanelContent() {
return (
<CardFrame title="Admin controls" actions={actions} bodyClassName="space-y-0.5 text-base">
<div className="flex gap-0.5 text-xs">
<button
type="button"
onClick={handleServerReboot}
disabled={serverRebooting}
className="button-danger disabled:cursor-not-allowed disabled:opacity-60"
>
{serverRebooting ? 'Server rebooting...' : 'Reboot Server'}
</button>
<button
type="button"
onClick={handleTestRewardOverlay}
-1
View File
@@ -392,7 +392,6 @@ export function SessionProvider({ children }) {
// intent, then the server checks privileges and fans out the same fixed
// update command to every currently connected rover.
updateAllRovers: () => emitWithAck('command:updateAllRovers'),
rebootServer: () => emitWithAck('server:reboot'),
playUploadedAudio: ({ roverId, name, mime, dataBase64 }) =>
emitWithAck('audio:uploadPlay', { roverId, name, mime, dataBase64 }),
stopUploadedAudio: (roverId) => emitWithAck('audio:uploadStop', { roverId }),