This commit is contained in:
2026-06-04 14:58:54 +07:00
parent ce3b3c900a
commit 4b65838f0f
2 changed files with 42 additions and 17 deletions

View File

@@ -196,6 +196,8 @@ function App() {
const agentCommand = latestAgentPackage?.installCommand || installCommand; const agentCommand = latestAgentPackage?.installCommand || installCommand;
const clientOs = useMemo(() => detectClientOs(), []); const clientOs = useMemo(() => detectClientOs(), []);
const isClientLinux = clientOs === 'linux'; const isClientLinux = clientOs === 'linux';
const isClientWindows = clientOs === 'windows';
const canShowAgentCommand = isClientLinux || isClientWindows;
const clientOsLabel = getClientOsLabel(clientOs); const clientOsLabel = getClientOsLabel(clientOs);
const installedByAppId = useMemo(() => { const installedByAppId = useMemo(() => {
@@ -288,7 +290,9 @@ function App() {
setInstalledApps([]); setInstalledApps([]);
setAgentStatus({ setAgentStatus({
state: 'warning', state: 'warning',
message: `Current client is ${clientOsLabel}. Web Client only supports Linux.` message: isClientWindows
? 'Windows detected. Copy the Agent command and run it in Ubuntu SSH.'
: `Current client is ${clientOsLabel}. Web Client only supports Linux.`
}); });
return false; return false;
} }
@@ -313,7 +317,7 @@ function App() {
setAgentStatus({ state: 'danger', message: getErrorMessage(error) }); setAgentStatus({ state: 'danger', message: getErrorMessage(error) });
return false; return false;
} }
}, [agentBaseUrl, clientOsLabel, isClientLinux]); }, [agentBaseUrl, clientOsLabel, isClientLinux, isClientWindows]);
const refreshAll = useCallback(async () => { const refreshAll = useCallback(async () => {
await Promise.all([refreshPackage(), refreshAgent()]); await Promise.all([refreshPackage(), refreshAgent()]);
@@ -497,7 +501,7 @@ function App() {
}, [draftSettings, notify]); }, [draftSettings, notify]);
const copyInstallCommand = useCallback(async () => { const copyInstallCommand = useCallback(async () => {
if (!isClientLinux) { if (!canShowAgentCommand) {
notify('warning', 'Lệnh cài Agent chỉ hiển thị trên máy Linux.'); notify('warning', 'Lệnh cài Agent chỉ hiển thị trên máy Linux.');
return; return;
} }
@@ -519,7 +523,7 @@ function App() {
} catch { } catch {
notify('warning', 'Không thể copy tự động trong browser này'); notify('warning', 'Không thể copy tự động trong browser này');
} }
}, [agentCommand, agentNeedsUpdate, isClientLinux, notify]); }, [agentCommand, agentNeedsUpdate, canShowAgentCommand, notify]);
useEffect(() => { useEffect(() => {
refreshAll(); refreshAll();
@@ -603,8 +607,8 @@ function App() {
<div className="nav-label">Runtime</div> <div className="nav-label">Runtime</div>
<StatusBox <StatusBox
icon={agentHealth ? PlugZap : WifiOff} icon={agentHealth ? PlugZap : WifiOff}
title={!isClientLinux ? 'Linux client required' : (agentNeedsUpdate ? 'Agent update available' : (agentHealth ? 'Agent online' : 'Agent offline'))} title={!isClientLinux ? (isClientWindows ? 'SSH install mode' : 'Linux client required') : (agentNeedsUpdate ? 'Agent update available' : (agentHealth ? 'Agent online' : 'Agent offline'))}
detail={!isClientLinux ? `${clientOsLabel} detected` : (agentHealth ? `${agentHealth.hostname || 'localhost'} · ${agentHealth.agentVersion || '-'}${agentNeedsUpdate ? ` -> ${latestAgentPackage.version}` : ''}` : '127.0.0.1:5010')} detail={!isClientLinux ? (isClientWindows ? 'Windows detected · run command on Ubuntu SSH' : `${clientOsLabel} detected`) : (agentHealth ? `${agentHealth.hostname || 'localhost'} · ${agentHealth.agentVersion || '-'}${agentNeedsUpdate ? ` -> ${latestAgentPackage.version}` : ''}` : '127.0.0.1:5010')}
tone={!isClientLinux ? 'warning' : (agentNeedsUpdate ? 'warning' : (agentHealth ? 'success' : 'danger'))} tone={!isClientLinux ? 'warning' : (agentNeedsUpdate ? 'warning' : (agentHealth ? 'success' : 'danger'))}
/> />
<StatusBox <StatusBox
@@ -636,7 +640,7 @@ function App() {
<header className="topbar"> <header className="topbar">
<div className="topbar-title"> <div className="topbar-title">
<span>robot.installer</span> <span>robot.installer</span>
<strong>{!isClientLinux ? 'Linux client required' : (agentHealth ? 'Ready for install' : 'Waiting for Agent')}</strong> <strong>{!isClientLinux ? (isClientWindows ? 'Copy command for Ubuntu SSH' : 'Linux client required') : (agentHealth ? 'Ready for install' : 'Waiting for Agent')}</strong>
</div> </div>
<div className="topbar-actions"> <div className="topbar-actions">
<a className="icon-button" href={joinUrl(packageBaseUrl, '/api/apps')} target="_blank" rel="noreferrer" title="Open package API"> <a className="icon-button" href={joinUrl(packageBaseUrl, '/api/apps')} target="_blank" rel="noreferrer" title="Open package API">
@@ -655,16 +659,18 @@ function App() {
<h1>Application catalog</h1> <h1>Application catalog</h1>
<p>Released apps từ package server trạng thái cài đặt trên máy local.</p> <p>Released apps từ package server trạng thái cài đặt trên máy local.</p>
</div> </div>
{isClientLinux && ( {canShowAgentCommand && (
<div className="page-actions"> <div className="page-actions">
<button className="btn btn-secondary" type="button" onClick={copyInstallCommand}> <button className="btn btn-secondary" type="button" onClick={copyInstallCommand}>
<Clipboard size={15} aria-hidden="true" /> <Clipboard size={15} aria-hidden="true" />
{agentNeedsUpdate ? 'Copy Agent update' : 'Copy Agent command'} {agentNeedsUpdate ? 'Copy Agent update' : 'Copy Agent command'}
</button> </button>
{isClientLinux && (
<a className="btn btn-primary" href={joinUrl(packageBaseUrl, '/install-agent.sh')} target="_blank" rel="noreferrer"> <a className="btn btn-primary" href={joinUrl(packageBaseUrl, '/install-agent.sh')} target="_blank" rel="noreferrer">
<Download size={15} aria-hidden="true" /> <Download size={15} aria-hidden="true" />
{agentNeedsUpdate ? 'update-agent.sh' : 'install-agent.sh'} {agentNeedsUpdate ? 'update-agent.sh' : 'install-agent.sh'}
</a> </a>
)}
</div> </div>
)} )}
</div> </div>
@@ -677,7 +683,13 @@ function App() {
</div> </div>
{!isClientLinux && ( {!isClientLinux && (
<ClientOsNotice osLabel={clientOsLabel} /> <ClientOsNotice
agentCommand={agentCommand}
canShowCommand={canShowAgentCommand}
isWindows={isClientWindows}
onCopyCommand={copyInstallCommand}
osLabel={clientOsLabel}
/>
)} )}
{isClientLinux && !agentHealth && ( {isClientLinux && !agentHealth && (
@@ -990,16 +1002,25 @@ function MetricCard({ label, value, note, tone }) {
); );
} }
function ClientOsNotice({ osLabel }) { function ClientOsNotice({ agentCommand, canShowCommand, isWindows, onCopyCommand, osLabel }) {
return ( return (
<div className="offline-banner client-os-banner"> <div className={`offline-banner client-os-banner ${canShowCommand ? 'command-visible' : ''}`}>
<AlertCircle size={19} aria-hidden="true" /> <AlertCircle size={19} aria-hidden="true" />
<div> <div>
<strong>Web Client chỉ hỗ trợ máy Linux</strong> <strong>{isWindows ? 'Cài Agent qua Ubuntu SSH' : 'Web Client chỉ hỗ trợ máy Linux'}</strong>
<p> <p>
Máy hiện tại được nhận diện {osLabel}. Lệnh cài Agent các thao tác cài đặt đã được ẩn. {isWindows
? 'Máy hiện tại là Windows. Hãy SSH vào Ubuntu server rồi chạy lệnh bên dưới trong terminal Ubuntu.'
: `Máy hiện tại được nhận diện là ${osLabel}. Lệnh cài Agent và các thao tác cài đặt đã được ẩn.`}
</p> </p>
{canShowCommand && <code>{agentCommand}</code>}
</div> </div>
{canShowCommand && (
<button className="btn btn-secondary" type="button" onClick={onCopyCommand}>
<Clipboard size={15} aria-hidden="true" />
Copy
</button>
)}
</div> </div>
); );
} }

View File

@@ -551,6 +551,10 @@ code {
grid-template-columns: auto minmax(0, 1fr); grid-template-columns: auto minmax(0, 1fr);
} }
.client-os-banner.command-visible {
grid-template-columns: auto minmax(0, 1fr) auto;
}
.offline-banner strong { .offline-banner strong {
color: #111827; color: #111827;
display: block; display: block;