Fix bugs found in full security and stability audit.

Harden artifact paths and fusion uploads, repair pool reconnect and login ID tracking, fix agent/fusion/frontend regressions, and refresh PROBLEMS.md with the full findings list.
This commit is contained in:
drjones
2026-05-29 09:57:22 -07:00
parent e11fb30350
commit f9e26bb1a6
13 changed files with 281 additions and 82 deletions

View File

@@ -9,6 +9,8 @@ interface Props {
onChange: (next: FleetFilterState) => void;
selectedCount: number;
onBulkAction: (action: string) => void;
onSelectAllFiltered?: () => void;
filteredCount?: number;
bulkBusy: boolean;
}
@@ -18,6 +20,8 @@ export default function FleetToolbar({
onChange,
selectedCount,
onBulkAction,
onSelectAllFiltered,
filteredCount,
bulkBusy,
}: Props) {
const tags = collectFleetTags(agents);
@@ -78,6 +82,14 @@ export default function FleetToolbar({
</label>
</div>
{onSelectAllFiltered && (filteredCount ?? 0) > 0 && selectedCount === 0 && (
<div className="fleet-bulk-bar">
<button type="button" className="btn btn-outline btn-sm" onClick={onSelectAllFiltered}>
Select all filtered ({filteredCount})
</button>
</div>
)}
{selectedCount > 0 && (
<div className="fleet-bulk-bar">
<span className="font-tech">{selectedCount} selected</span>

View File

@@ -11,6 +11,7 @@ export default function SessionGate({ children }: { children: ReactNode }) {
useEffect(() => {
const token = getStoredAuth();
if (!token) {
setAuthed(false);
setReady(true);
return;
}
@@ -19,20 +20,27 @@ export default function SessionGate({ children }: { children: ReactNode }) {
setAuthed(r.ok);
setReady(true);
})
.catch(() => setReady(true));
.catch(() => {
setAuthed(false);
setReady(true);
});
}, []);
const handleLogin = async (e: React.FormEvent) => {
e.preventDefault();
setErr('');
const token = btoa(`${user}:${pass}`);
const res = await fetch('/api/v1/config', { headers: { Authorization: `Basic ${token}` } });
if (!res.ok) {
setErr('Login failed — check username and password.');
return;
try {
const res = await fetch('/api/v1/config', { headers: { Authorization: `Basic ${token}` } });
if (!res.ok) {
setErr('Login failed — check username and password.');
return;
}
setStoredAuth(user, pass);
setAuthed(true);
} catch {
setErr('Cannot reach server — check that miner-server is running.');
}
setStoredAuth(user, pass);
setAuthed(true);
};
if (!ready) {

View File

@@ -35,6 +35,16 @@ export function useWebSocket(): UseWebSocketReturn {
const connect = useCallback(() => {
if (unmounted.current) return;
if (reconnectTimer.current) {
clearTimeout(reconnectTimer.current);
reconnectTimer.current = null;
}
const existing = wsRef.current;
if (existing && (existing.readyState === WebSocket.OPEN || existing.readyState === WebSocket.CONNECTING)) {
existing.close();
}
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${protocol}//${window.location.host}/ws/dashboard`;
@@ -48,6 +58,7 @@ export function useWebSocket(): UseWebSocketReturn {
ws.onclose = () => {
if (unmounted.current) return;
setIsConnected(false);
if (reconnectTimer.current) clearTimeout(reconnectTimer.current);
reconnectTimer.current = setTimeout(connect, 3000);
};

View File

@@ -45,6 +45,12 @@ export default function AgentsPage() {
.finally(() => setLoading(false));
}, []);
useEffect(() => {
if (!selectedAgent) return;
setNotesDraft(selectedAgent.notes || '');
setTagsDraft((selectedAgent.tags || []).join(', '));
}, [selectedAgent?.id]);
useEffect(() => {
if (!isConnected) return;
setAgents(liveAgents);
@@ -52,8 +58,6 @@ export default function AgentsPage() {
const updated = liveAgents.find((a) => a.id === selectedAgent.id);
if (updated) {
setSelectedAgent(updated);
setNotesDraft(updated.notes || '');
setTagsDraft((updated.tags || []).join(', '));
} else {
setSelectedAgent(null);
setLogContent('');

View File

@@ -128,6 +128,9 @@ export default function DashboardPage() {
setBulkBusy(true);
try {
await api.sendBulkCommand(onlineIds, action);
} catch (err) {
console.error(err);
alert(err instanceof Error ? err.message : 'Bulk command failed');
} finally {
setBulkBusy(false);
}
@@ -269,6 +272,8 @@ export default function DashboardPage() {
filters={filters}
onChange={setFilters}
selectedCount={selectedIds.size}
filteredCount={filteredAgents.length}
onSelectAllFiltered={() => setSelectedIds(new Set(filteredAgents.map((a) => a.id)))}
onBulkAction={handleBulkAction}
bulkBusy={bulkBusy}
/>