'use client'; import React, { useState, useEffect, useRef, useCallback } from 'react'; import { OperationsRole, OpsAction, OpsActionType, OpsActionState, OpsActionCapabilities, OpsActionsResponse, } from './types'; interface OperationsActionsProps { role: OperationsRole; onActionCompleted?: () => void; } const ACTION_CONFIGS: Record< OpsActionType, { title: string; description: string; icon: string; destructive: boolean; requiredConfirmation?: string; } > = { deploy_website: { title: 'Deploy Website', description: 'Pulls latest master commit, builds Next.js production bundle, verifies health, and updates current symlink with automatic rollback.', icon: 'rocket_launch', destructive: false, }, restart_website: { title: 'Restart Website', description: 'Gracefully restarts the motorsignal-website systemd service and verifies /api/version ingress readiness.', icon: 'restart_alt', destructive: false, }, restart_api: { title: 'Restart API', description: 'Initiates detached systemd restart of motorsignal-api. In-flight requests may terminate. Type confirmation below.', icon: 'sync_problem', destructive: true, requiredConfirmation: 'RESTART API', }, reboot_server: { title: 'Reboot Host Server', description: 'Schedules full host server reboot. All services, website, and API traffic will be temporarily unavailable. Type confirmation below.', icon: 'power_settings_new', destructive: true, requiredConfirmation: 'REBOOT MOTORSIGNAL SERVER', }, }; const ACTIVE_STATES: OpsActionState[] = [ 'queued', 'preflight', 'running', 'verifying', 'rollback_running', ]; interface StepperPhase { name: string; desc: string; } const DEPLOY_PHASES: StepperPhase[] = [ { name: 'Preflight Checks', desc: 'Validating system bounds and working tree' }, { name: 'Release Staging', desc: 'Preparing release runtime directory' }, { name: 'Production Build', desc: 'Compiling Next.js production bundle' }, { name: 'Service Cutover', desc: 'Activating symlink and restarting service' }, { name: 'Health Verification', desc: 'Probing loopback and public /api/version' }, ]; const SERVICE_PHASES: StepperPhase[] = [ { name: 'Preflight Checks', desc: 'Validating role permissions and locks' }, { name: 'Signal Dispatch', desc: 'Sending control signal to systemd supervisor' }, { name: 'Service Cycle', desc: 'Restarting process and binding listeners' }, { name: 'Health Probe', desc: 'Verifying application readiness probe' }, ]; export function OperationsActions({ role, onActionCompleted }: OperationsActionsProps) { const [capabilities, setCapabilities] = useState(null); const [actions, setActions] = useState([]); const [activeAction, setActiveAction] = useState(null); const [loading, setLoading] = useState(true); const [historyError, setHistoryError] = useState(null); // In-flight Action & Live Execution Tracking State const [inFlightAction, setInFlightAction] = useState<{ actionType: OpsActionType; startTime: number; } | null>(null); const [completedAction, setCompletedAction] = useState(null); const [elapsedSeconds, setElapsedSeconds] = useState(0); const [modalMode, setModalMode] = useState<'confirm' | 'progress' | 'completed'>('confirm'); const [modalActionType, setModalActionType] = useState(null); const [isModalOpen, setIsModalOpen] = useState(false); const [isMinimized, setIsMinimized] = useState(false); const [copiedSha, setCopiedSha] = useState(false); const [recentBannerAction, setRecentBannerAction] = useState(null); // Modal Form Inputs const [confirmationInput, setConfirmationInput] = useState(''); const [isSubmitting, setIsSubmitting] = useState(false); const [submitError, setSubmitError] = useState(null); const pollTimeoutRef = useRef(null); const isOwner = role === 'owner'; const actionStartTimeRef = useRef(null); // Live Timer: Calculates real elapsed seconds from wall clock via setTimeout recursion useEffect(() => { let timer: NodeJS.Timeout | null = null; if (inFlightAction) { if (!actionStartTimeRef.current) { actionStartTimeRef.current = inFlightAction.startTime; } } else if (activeAction) { if (!actionStartTimeRef.current) { const parsed = activeAction.request_timestamp ? new Date(activeAction.request_timestamp).getTime() : Date.now(); actionStartTimeRef.current = isNaN(parsed) ? Date.now() : parsed; } } else { actionStartTimeRef.current = null; setElapsedSeconds(0); return; } const tick = () => { if (actionStartTimeRef.current) { const sec = Math.max(0, Math.floor((Date.now() - actionStartTimeRef.current) / 1000)); setElapsedSeconds(sec); } timer = setTimeout(() => tick(), 1000); }; tick(); return () => { if (timer) clearTimeout(timer); }; }, [inFlightAction?.startTime, activeAction?.id, activeAction?.request_timestamp]); // Fetch capabilities & recent action history const fetchActions = useCallback(async () => { try { const resp = await fetch('/api/ops/actions?limit=10', { headers: { accept: 'application/json' }, cache: 'no-store', }); if (resp.ok) { const data = await resp.json(); const actionList: OpsAction[] = Array.isArray(data) ? data : Array.isArray((data as OpsActionsResponse)?.actions) ? (data as OpsActionsResponse).actions : []; setActions(actionList); setHistoryError(null); // Check if there is an active action const active = actionList.find((a) => ACTIVE_STATES.includes(a.state)); if (active) { setActiveAction(active); } else { setActiveAction(null); } } else { setHistoryError('Unable to load operations action history.'); } } catch { setHistoryError('Network error connecting to operations actions platform.'); } finally { setLoading(false); } }, []); const fetchCapabilities = useCallback(async () => { try { const resp = await fetch('/api/ops/actions/capabilities', { headers: { accept: 'application/json' }, cache: 'no-store', }); if (resp.ok) { const data: OpsActionCapabilities = await resp.json(); setCapabilities(data); } } catch { // Non-blocking } }, []); useEffect(() => { void fetchCapabilities(); void fetchActions(); }, [fetchCapabilities, fetchActions]); // Polling when an action is active useEffect(() => { let isCancelled = false; if (activeAction) { const poll = async () => { try { const resp = await fetch(`/api/ops/actions/${encodeURIComponent(activeAction.id)}`, { headers: { accept: 'application/json' }, cache: 'no-store', }); if (resp.ok && !isCancelled) { const updated: OpsAction = await resp.json(); if (!ACTIVE_STATES.includes(updated.state)) { // Action finished! setActiveAction(null); setInFlightAction(null); actionStartTimeRef.current = null; setCompletedAction(updated); setRecentBannerAction(updated); setModalMode('completed'); void fetchActions(); if (onActionCompleted) onActionCompleted(); return; } else { setActiveAction(updated); } } } catch { // Non-blocking error across service cycling } if (!isCancelled) { pollTimeoutRef.current = setTimeout(() => { void poll(); }, 1500); } }; pollTimeoutRef.current = setTimeout(() => { void poll(); }, 1500); } return () => { isCancelled = true; if (pollTimeoutRef.current) { clearTimeout(pollTimeoutRef.current); pollTimeoutRef.current = null; } }; }, [activeAction?.id, activeAction?.state, fetchActions, onActionCompleted]); const handleOpenModal = (actionType: OpsActionType) => { if (!isOwner || activeAction || inFlightAction) return; setModalActionType(actionType); setModalMode('confirm'); setIsModalOpen(true); setIsMinimized(false); setConfirmationInput(''); setSubmitError(null); setCompletedAction(null); }; const handleCloseModal = () => { if (inFlightAction || (activeAction && modalMode === 'progress')) { // Minimize instead of terminating monitor view setIsMinimized(true); setIsModalOpen(false); return; } setIsModalOpen(false); setIsMinimized(false); setModalActionType(null); setConfirmationInput(''); setSubmitError(null); }; const handleSubmitAction = async (e: React.FormEvent) => { e.preventDefault(); if (!modalActionType || !isOwner || isSubmitting) return; const config = ACTION_CONFIGS[modalActionType]; if ( config.requiredConfirmation && confirmationInput.trim() !== config.requiredConfirmation ) { setSubmitError(`Please type exact confirmation text: "${config.requiredConfirmation}"`); return; } setIsSubmitting(true); setSubmitError(null); setElapsedSeconds(0); setCompletedAction(null); // Transition modal into LIVE PROGRESS tracking mode setModalMode('progress'); setInFlightAction({ actionType: modalActionType, startTime: Date.now(), }); const idempotencyKey = `ops-action-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`; try { const resp = await fetch('/api/ops/actions', { method: 'POST', headers: { 'content-type': 'application/json', accept: 'application/json', }, body: JSON.stringify({ action_type: modalActionType, confirmation: confirmationInput.trim() || undefined, confirmation_text: confirmationInput.trim() || undefined, idempotency_key: idempotencyKey, }), }); const data: (OpsAction & { detail?: string }) | null = await resp.json().catch(() => null); if (resp.ok && data) { if (ACTIVE_STATES.includes(data.state)) { setActiveAction(data); } else { // Action completed synchronously setActiveAction(null); setInFlightAction(null); setCompletedAction(data); setRecentBannerAction(data); setModalMode('completed'); void fetchActions(); if (onActionCompleted) onActionCompleted(); } } else if (resp.status === 409) { setInFlightAction(null); setModalMode('confirm'); setSubmitError(data?.detail || 'Another operations action is currently active.'); } else if (resp.status === 403) { setInFlightAction(null); setModalMode('confirm'); setSubmitError(data?.detail || 'Owner role authority required.'); } else { setInFlightAction(null); setModalMode('confirm'); setSubmitError(data?.detail || 'Failed to trigger operations action.'); } } catch { setInFlightAction(null); setModalMode('confirm'); setSubmitError('Network failure submitting action request.'); } finally { setIsSubmitting(false); } }; const handleCopySha = (sha: string) => { void navigator.clipboard.writeText(sha); setCopiedSha(true); setTimeout(() => setCopiedSha(false), 2000); }; // Helper to compute active phase index const currentActionType = modalActionType || activeAction?.action_type || inFlightAction?.actionType || completedAction?.action_type || 'deploy_website'; const isDeploy = currentActionType === 'deploy_website'; const phases = isDeploy ? DEPLOY_PHASES : SERVICE_PHASES; const getActivePhaseIndex = (): number => { if (completedAction) { return completedAction.state === 'succeeded' ? phases.length : Math.max(0, phases.length - 2); } const state = activeAction?.state; if (state === 'verifying' || state === 'rollback_running') { return phases.length - 1; } if (state === 'running') { if (isDeploy) { if (elapsedSeconds < 4) return 1; if (elapsedSeconds < 14) return 2; return 3; } else { if (elapsedSeconds < 2) return 1; if (elapsedSeconds < 5) return 2; return 3; } } if (state === 'preflight' || elapsedSeconds > 1) { return 1; } return 0; }; const currentPhaseIndex = getActivePhaseIndex(); const getStateBadge = (state: OpsActionState) => { switch (state) { case 'succeeded': return ( check_circle Succeeded ); case 'rolled_back': return ( undo Rolled Back ); case 'failed': case 'rollback_failed': return ( error {state === 'rollback_failed' ? 'Rollback Failed' : 'Failed'} ); case 'queued': case 'preflight': case 'running': case 'verifying': case 'rollback_running': return ( progress_activity {state.replace('_', ' ').toUpperCase()} ); default: return ( {state} ); } }; const formatElapsed = (sec: number) => { const m = Math.floor(sec / 60); const s = sec % 60; return `${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}s`; }; return (
{/* Recent Action Completion Banner */} {recentBannerAction && (
{recentBannerAction.state === 'succeeded' ? 'task_alt' : recentBannerAction.state === 'rolled_back' ? 'undo' : 'error'}
{ACTION_CONFIGS[recentBannerAction.action_type]?.title || recentBannerAction.action_type}{' '} {recentBannerAction.state === 'succeeded' ? 'Finished Successfully' : recentBannerAction.state === 'rolled_back' ? 'Rolled Back to Previous Release' : 'Execution Failed'} {getStateBadge(recentBannerAction.state)}

{recentBannerAction.deployed_revision ? ( Deployed Revision:{' '} {recentBannerAction.deployed_revision.substring(0, 10)} {' '} — {recentBannerAction.status_message} ) : ( recentBannerAction.status_message || 'Action execution completed.' )}

)} {/* Active Action Progress HUD Banner */} {activeAction && (
progress_activity

Action in Progress: {ACTION_CONFIGS[activeAction.action_type]?.title || activeAction.action_type} {getStateBadge(activeAction.state)}

{activeAction.status_message || 'Executing action across system boundary...'}

timer {formatElapsed(elapsedSeconds)}
{/* Stepper Progress Bar */}
Phase {currentPhaseIndex + 1} of {phases.length}: {phases[currentPhaseIndex]?.name} {Math.round(((currentPhaseIndex + 1) / phases.length) * 100)}%
)} {/* Floating Docked HUD Widget (Bottom-Right) */} {isMinimized && (inFlightAction || activeAction || completedAction) && (
{completedAction ? ( {completedAction.state === 'succeeded' ? 'check_circle' : 'error'} ) : ( progress_activity )}
{ACTION_CONFIGS[currentActionType]?.title || currentActionType} {completedAction && ( {completedAction.state} )}
{completedAction ? completedAction.deployed_revision ? `Rev: ${completedAction.deployed_revision.substring(0, 8)}` : completedAction.status_message : `Running (${formatElapsed(elapsedSeconds)})`}
{completedAction && ( )}
)} {/* Role Notice for Operators */} {!isOwner && (
lock Owner role required to execute operational actions. Operator accounts have read-only visibility into action status and history.
)} {/* Execution Boundary / Helper Status Notice */} {capabilities && !capabilities.helper_configured && (
warning Action execution boundary helper is unconfigured or not executable on this host. Mutating actions will be rejected by backend policy.
)} {/* Action Trigger Cards */}
{(Object.keys(ACTION_CONFIGS) as OpsActionType[]).map((actionKey) => { const config = ACTION_CONFIGS[actionKey]; const isThisActive = (activeAction?.action_type === actionKey) || (inFlightAction?.actionType === actionKey); const isDisabled = !isOwner || !!activeAction || !!inFlightAction; return (
{config.icon}

{config.title}

{isThisActive && ( progress_activity Active )} {config.destructive && ( Guarded )}

{config.description}

{actionKey}
); })}
{/* Action History Section */}

Operations Action History

Audited system actions and deployments executed across privileged execution boundary.

{historyError && (
{historyError}
)} {actions.length === 0 && !loading && (
No operations actions have been executed yet.
)} {actions.length > 0 && (
{actions.map((act) => ( ))}
Timestamp Action Actor Status Details Revision
{new Date(act.request_timestamp).toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit', })} {ACTION_CONFIGS[act.action_type]?.title || act.action_type} {act.actor_identity} {getStateBadge(act.state)} {act.status_message || (act.failure_code ? `Error: ${act.failure_code}` : '—')} {act.deployed_revision ? act.deployed_revision.substring(0, 8) : '—'}
)}
{/* Interactive Action Modal (Confirm -> Progress -> Completed) */} {isModalOpen && modalActionType && (
{/* Modal Header */}
{modalMode === 'completed' ? completedAction?.state === 'succeeded' ? 'check_circle' : 'error' : modalMode === 'progress' ? 'progress_activity' : ACTION_CONFIGS[modalActionType].icon}

{modalMode === 'completed' ? `${ACTION_CONFIGS[modalActionType].title}: Execution ${ completedAction?.state === 'succeeded' ? 'Succeeded' : 'Finished' }` : modalMode === 'progress' ? `Executing ${ACTION_CONFIGS[modalActionType].title}` : `Confirm ${ACTION_CONFIGS[modalActionType].title}`}

{modalMode === 'progress' ? `Execution in progress across system boundary • ${formatElapsed(elapsedSeconds)}` : modalMode === 'completed' ? `Final state recorded in audit stream • ${completedAction?.state || 'done'}` : 'Strict operational security boundary authorization'}

{modalMode === 'progress' && ( )} {modalMode !== 'progress' && ( )}
{/* Modal Body: MODE 1 - CONFIRMATION FORM */} {modalMode === 'confirm' && (
{submitError && (
error {submitError}
)} {capabilities && !capabilities.helper_configured && (
warning Action execution boundary helper (ops-action-helper.sh) is not yet configured on this host. Actions will be rejected by backend policy.
)}

{ACTION_CONFIGS[modalActionType].description}

{ACTION_CONFIGS[modalActionType].requiredConfirmation && (
setConfirmationInput(e.target.value)} placeholder={ACTION_CONFIGS[modalActionType].requiredConfirmation} className="w-full px-3 py-2 bg-surface-container-lowest border border-outline-variant/40 rounded-xl text-sm font-mono text-on-surface focus:outline-none focus:ring-2 focus:ring-rose-500/50" />
)}
)} {/* Modal Body: MODE 2 - IN-PROGRESS TRACKER */} {modalMode === 'progress' && (
{/* Active Phase & Timer Header */}
progress_activity
Current Step: {phases[currentPhaseIndex]?.name}
{activeAction?.status_message || phases[currentPhaseIndex]?.desc}
timer {formatElapsed(elapsedSeconds)}
{/* Progress Bar */}
Execution Pipeline {Math.round(((currentPhaseIndex + 1) / phases.length) * 100)}%
{/* Stepper Pipeline */}
{phases.map((phase, idx) => { const isCompleted = idx < currentPhaseIndex; const isCurrent = idx === currentPhaseIndex; const isUpcoming = idx > currentPhaseIndex; return (
{isCompleted ? ( check ) : isCurrent ? ( progress_activity ) : ( idx + 1 )}
{phase.name}
{phase.desc}
{isCurrent && ( Processing )} {isCompleted && ( Done )}
); })}
{/* Footer Controls */}
Do not refresh this tab while execution is in progress.
)} {/* Modal Body: MODE 3 - COMPLETION SUMMARY */} {modalMode === 'completed' && completedAction && (
{completedAction.state === 'succeeded' ? (
check_circle

{ACTION_CONFIGS[modalActionType].title} Succeeded!

{completedAction.status_message || 'Action executed and verified across execution boundary.'}

Execution Duration {formatElapsed(elapsedSeconds || 12)}
Service Status Active & Verified (200 OK)
{completedAction.deployed_revision && (
Deployed Git Revision {completedAction.deployed_revision}
)}
) : (
error

{ACTION_CONFIGS[modalActionType].title} Failed

{completedAction.status_message || 'Action encountered an error during execution.'}

Failure Code {completedAction.failure_code || 'unknown'}
Rollback State {completedAction.rollback_status || 'none'}
{completedAction.state === 'rolled_back' && (
undo Automatic Rollback Succeeded: The previous stable release was restored safely.
)}
)} {/* Footer Controls */}
{completedAction.state === 'succeeded' && isDeploy && ( open_in_new Open Website )} {completedAction.state !== 'succeeded' && ( )}
)}
)}
); }