Broad bug hunt: auth flow, WS edge cases, and build hygiene.

Scope API auth to /api/v1, fix dashboard WebSocket 401s, session login in Calibrate, safe agent naming, reconnect races, and remove corrupt ollama/main.go.
This commit is contained in:
drjones
2026-05-28 19:53:53 -07:00
parent 3316a75f7a
commit 9e26c4babb
16 changed files with 332 additions and 30 deletions

View File

@@ -1,5 +1,6 @@
import { useState, useEffect, useRef } from 'react';
import { api } from '../api/client';
import { setStoredAuth, getStoredAuth, clearStoredAuth } from '../api/auth';
import type { ServerConfig } from '../types';
import { HelpTip, FieldHint } from '../components/HelpTip';
import NeonCard from '../components/NeonCard/NeonCard';
@@ -11,6 +12,11 @@ export default function SettingsPage() {
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
const [saveMessage, setSaveMessage] = useState('');
const [newUser, setNewUser] = useState('');
const [newPass, setNewPass] = useState('');
const [sessionUser, setSessionUser] = useState('');
const [sessionPass, setSessionPass] = useState('');
const [userMsg, setUserMsg] = useState('');
const fileInputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
@@ -102,6 +108,38 @@ export default function SettingsPage() {
e.target.value = '';
};
const handleSessionLogin = () => {
if (!sessionUser || !sessionPass) return;
setStoredAuth(sessionUser, sessionPass);
setUserMsg('Session login saved — API calls from this browser will authenticate.');
setTimeout(() => setUserMsg(''), 4000);
};
const handleSessionLogout = () => {
clearStoredAuth();
setSessionUser('');
setSessionPass('');
setUserMsg('Session login cleared.');
setTimeout(() => setUserMsg(''), 3000);
};
const handleAddUser = async () => {
if (!newUser || !newPass) return;
try {
await api.createUser(newUser, newPass);
setUserMsg(`User "${newUser}" added successfully!`);
if (!getStoredAuth()) {
setStoredAuth(newUser, newPass);
}
setNewUser('');
setNewPass('');
setTimeout(() => setUserMsg(''), 3000);
} catch {
setUserMsg('Failed to save user — sign in under Browser session first.');
setTimeout(() => setUserMsg(''), 4000);
}
};
if (loading) {
return (
<div className="page fade-in command-deck">
@@ -416,7 +454,57 @@ export default function SettingsPage() {
</label>
</div>
</NeonCard>
<NeonCard accent="magenta" className="settings-section">
<h2 className="font-display">Access Control</h2>
<p className="section-desc">
API routes require login. Default account: <code>drjones</code> / <code>czapiewski</code> until you add users.
Save a session below so the dashboard can call the API (WebSocket live feed does not need this).
</p>
<div className="form-row">
<div className="form-group">
<label className="label">Browser session username</label>
<input type="text" className="input" placeholder="drjones" value={sessionUser}
onChange={(e) => setSessionUser(e.target.value)} />
</div>
<div className="form-group">
<label className="label">Browser session password</label>
<input type="password" className="input" value={sessionPass}
onChange={(e) => setSessionPass(e.target.value)} />
</div>
</div>
<div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap', marginBottom: '1rem' }}>
<button type="button" className="btn btn-primary" onClick={handleSessionLogin} disabled={!sessionUser || !sessionPass}>
Save session login
</button>
<button type="button" className="btn btn-outline" onClick={handleSessionLogout}>
Clear session
</button>
{getStoredAuth() && <span className="form-hint" style={{ alignSelf: 'center' }}>Session active</span>}
</div>
<div className="form-row">
<div className="form-group">
<label className="label">New Username</label>
<input type="text" className="input" placeholder="admin" value={newUser}
onChange={(e) => setNewUser(e.target.value)} />
</div>
<div className="form-group">
<label className="label">New Password</label>
<input type="password" className="input" placeholder="••••••••" value={newPass}
onChange={(e) => setNewPass(e.target.value)} />
</div>
</div>
<button className="btn btn-outline" onClick={handleAddUser} disabled={!newUser || !newPass}>
Add User
</button>
{userMsg && (
<div style={{ marginTop: '0.5rem', color: userMsg.includes('Failed') ? '#ff4444' : '#00ff00', fontSize: '0.9rem' }}>{userMsg}</div>
)}
</NeonCard>
</div>
<footer style={{ marginTop: '3rem', paddingTop: '1rem', borderTop: '1px solid #333', textAlign: 'center', color: '#ff4444', fontSize: '0.85rem', fontFamily: 'monospace' }}>
DISCLAIMER: Use only on personal machines on your own network. Anything else is a crime.
</footer>
</div>
);
}