fix: 2026-06-04 audit pass — README, USB pack, multi-area fixes
WS ticket dashboard auth, builder universal signing/size limits/fusion obfuscation/dropper bundles, Path Tracer WireGuard topology, SessionGate degraded mode and download timeouts, server bootstrap (data dir, cloudflared dedupe, config port precedence), agent mesh/miner/spread fixes. README refreshed; usb bundle repacked; PROBLEMS.md audit log updated.
This commit is contained in:
@@ -1,103 +1,159 @@
|
||||
import { useEffect, useState, type ReactNode } from 'react';
|
||||
import { getStoredAuth, setStoredAuth } from '../api/auth';
|
||||
import { useSound } from '../context/SoundContext';
|
||||
import { FlowerOfLifeWatermark, KnowledgeKey } from './Visual/sacredGeometry/motifs';
|
||||
|
||||
export default function SessionGate({ children }: { children: ReactNode }) {
|
||||
const { play } = useSound();
|
||||
const [ready, setReady] = useState(false);
|
||||
const [authed, setAuthed] = useState(!!getStoredAuth());
|
||||
const [user, setUser] = useState('');
|
||||
const [pass, setPass] = useState('');
|
||||
const [err, setErr] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
const token = getStoredAuth();
|
||||
if (!token) {
|
||||
setAuthed(false);
|
||||
setReady(true);
|
||||
return;
|
||||
}
|
||||
fetch('/api/v1/config', { headers: { Authorization: `Basic ${token}` } })
|
||||
.then((r) => {
|
||||
setAuthed(r.ok);
|
||||
setReady(true);
|
||||
})
|
||||
.catch(() => {
|
||||
setAuthed(false);
|
||||
setReady(true);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleLogin = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setErr('');
|
||||
const token = btoa(`${user}:${pass}`);
|
||||
try {
|
||||
const res = await fetch('/api/v1/config', { headers: { Authorization: `Basic ${token}` } });
|
||||
if (!res.ok) {
|
||||
setErr('Login failed — check username and password.');
|
||||
play('error');
|
||||
return;
|
||||
}
|
||||
setStoredAuth(user, pass);
|
||||
setAuthed(true);
|
||||
play('success');
|
||||
} catch {
|
||||
setErr('Cannot reach server — check that miner-server is running.');
|
||||
}
|
||||
};
|
||||
|
||||
if (!ready) {
|
||||
return (
|
||||
<div className="session-gate">
|
||||
<div className="session-gate-sacred-ring" aria-hidden>
|
||||
<FlowerOfLifeWatermark opacity={0.5} />
|
||||
</div>
|
||||
<p className="font-tech">Starting AetherForge…</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!authed) {
|
||||
return (
|
||||
<div className="session-gate">
|
||||
<div className="session-gate-sacred-ring" aria-hidden>
|
||||
<FlowerOfLifeWatermark opacity={0.5} />
|
||||
</div>
|
||||
<div className="session-gate-keys" aria-hidden>
|
||||
<div className="session-gate-key session-gate-key--tl">
|
||||
<KnowledgeKey opacity={0.55} />
|
||||
</div>
|
||||
<div className="session-gate-key session-gate-key--br">
|
||||
<KnowledgeKey opacity={0.45} />
|
||||
</div>
|
||||
</div>
|
||||
<form className="session-gate-card card" onSubmit={handleLogin}>
|
||||
<h1 className="font-display">AetherForge</h1>
|
||||
<p className="form-hint">Sign in to open the command deck.</p>
|
||||
<label className="label" htmlFor="session-user">Username</label>
|
||||
<input id="session-user" className="input" value={user} onChange={(e) => setUser(e.target.value)} autoComplete="username" />
|
||||
<label className="label" htmlFor="session-pass">Password</label>
|
||||
<input
|
||||
id="session-pass"
|
||||
className="input"
|
||||
type="password"
|
||||
value={pass}
|
||||
onChange={(e) => setPass(e.target.value)}
|
||||
autoComplete="current-password"
|
||||
/>
|
||||
{err && <p className="form-hint" style={{ color: 'var(--accent-red)' }}>{err}</p>}
|
||||
<button type="submit" className="btn btn-primary btn-lg">
|
||||
Enter Command Deck
|
||||
</button>
|
||||
<p className="session-gate-whisper" aria-hidden>
|
||||
ψ · the deck remembers every key
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
}
|
||||
import { useEffect, useState, type ReactNode } from 'react';
|
||||
|
||||
import {
|
||||
|
||||
AETHERFORGE_CLIENT_HEADER,
|
||||
|
||||
AETHERFORGE_CLIENT_VALUE,
|
||||
|
||||
authHeaders,
|
||||
|
||||
clearStoredAuth,
|
||||
|
||||
consumeAuthExpiredFlag,
|
||||
|
||||
encodeBasicToken,
|
||||
|
||||
getStoredAuth,
|
||||
|
||||
setStoredAuth,
|
||||
|
||||
} from '../api/auth';
|
||||
|
||||
import { useSound } from '../context/SoundContext';
|
||||
|
||||
import { FlowerOfLifeWatermark, KnowledgeKey } from './Visual/sacredGeometry/motifs';
|
||||
|
||||
|
||||
|
||||
export default function SessionGate({ children }: { children: ReactNode }) {
|
||||
|
||||
const { play } = useSound();
|
||||
|
||||
const [ready, setReady] = useState(false);
|
||||
|
||||
const [authed, setAuthed] = useState(!!getStoredAuth());
|
||||
|
||||
const [degraded, setDegraded] = useState(false);
|
||||
|
||||
const [user, setUser] = useState('');
|
||||
|
||||
const [pass, setPass] = useState('');
|
||||
|
||||
const [err, setErr] = useState('');
|
||||
|
||||
const [sessionExpired, setSessionExpired] = useState(false);
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
const sync = () => {
|
||||
|
||||
const hasAuth = !!getStoredAuth();
|
||||
|
||||
setAuthed(hasAuth);
|
||||
|
||||
if (!hasAuth) {
|
||||
|
||||
setSessionExpired(consumeAuthExpiredFlag());
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
window.addEventListener('aetherforge-auth', sync);
|
||||
|
||||
return () => window.removeEventListener('aetherforge-auth', sync);
|
||||
|
||||
}, []);
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
const token = getStoredAuth();
|
||||
|
||||
if (!token) {
|
||||
|
||||
setAuthed(false);
|
||||
|
||||
setSessionExpired(consumeAuthExpiredFlag());
|
||||
|
||||
setReady(true);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
fetch('/api/v1/config', { headers: authHeaders() })
|
||||
|
||||
.then((r) => {
|
||||
|
||||
if (r.status === 401) {
|
||||
|
||||
clearStoredAuth({ silent: true, expired: true });
|
||||
|
||||
setAuthed(false);
|
||||
|
||||
setSessionExpired(true);
|
||||
|
||||
} else if (!r.ok) {
|
||||
|
||||
// Server reachable but unhappy — keep saved credentials (degraded mode).
|
||||
|
||||
setAuthed(true);
|
||||
|
||||
setDegraded(true);
|
||||
|
||||
} else {
|
||||
|
||||
setAuthed(true);
|
||||
|
||||
setDegraded(false);
|
||||
|
||||
}
|
||||
|
||||
setReady(true);
|
||||
|
||||
})
|
||||
|
||||
.catch(() => {
|
||||
|
||||
// Network blip — trust stored credentials until the server responds.
|
||||
|
||||
setAuthed(true);
|
||||
|
||||
setDegraded(true);
|
||||
|
||||
setReady(true);
|
||||
|
||||
});
|
||||
|
||||
}, []);
|
||||
|
||||
|
||||
|
||||
const handleLogin = async (e: React.FormEvent) => {
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
setErr('');
|
||||
|
||||
setSessionExpired(false);
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
|
||||
[AETHERFORGE_CLIENT_HEADER]: AETHERFORGE_CLIENT_VALUE,
|
||||
|
||||
Authorization: `Basic ${encodeBasicToken(user, pass)}`,
|
||||
|
||||
};
|
||||
|
||||
try {
|
||||
|
||||
const res = await fetch('/api/v1/config', { headers });
|
||||
|
||||
if (!res.ok) {
|
||||
|
||||
setErr('Login failed — check username and password.');
|
||||
|
||||
Reference in New Issue
Block a user