Use email as host login credential; remove separate username field; v2.0.8
Publish Container / docker (push) Failing after 10m44s

- Login form now asks for email + password (no username)
- Admin creates hosts with name + email + password; email is always
  required and welcome email always sends on creation
- Duplicate email check replaces duplicate username check
- Admin table shows Email (login) column only; username field removed
  throughout data model, routes, and UI

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 4.6 committed 2026-09-08 14:05:02 -04:00
1 parent a12a471c41
commit 684181c34c
5 files changed
+33 -46

No files matched your search

+7 -15
View File
@@ -1100,7 +1100,6 @@ function QrCodeImage({ url, label }: { url: string; label: string }) {
interface UploadHost {
id: string
name: string
username: string
email: string
active: boolean
createdAt: string
@@ -1111,7 +1110,6 @@ function NuggetsUploadPortalAdmin() {
const [loading, setLoading] = useState(true)
const [error, setError] = useState('')
const [addName, setAddName] = useState('')
const [addUsername, setAddUsername] = useState('')
const [addEmail, setAddEmail] = useState('')
const [addPassword, setAddPassword] = useState('')
const [addError, setAddError] = useState('')
@@ -1142,12 +1140,12 @@ function NuggetsUploadPortalAdmin() {
const r = await fetch('/api/admin/nuggets-upload/hosts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: addName, username: addUsername, email: addEmail, password: addPassword }),
body: JSON.stringify({ name: addName, email: addEmail, password: addPassword }),
})
const d = await r.json()
if (!r.ok) { setAddError(d.message ?? 'Failed.'); return }
setAddEmailSent(Boolean(d.emailSent))
setAddName(''); setAddUsername(''); setAddEmail(''); setAddPassword('')
setAddName(''); setAddEmail(''); setAddPassword('')
reload()
} finally {
setAddSaving(false)
@@ -1212,8 +1210,7 @@ function NuggetsUploadPortalAdmin() {
<thead>
<tr style={{ borderBottom: '2px solid var(--color-border, #e5e0d5)' }}>
<th style={{ textAlign: 'left', padding: '6px 8px' }}>Name</th>
<th style={{ textAlign: 'left', padding: '6px 8px' }}>Username</th>
<th style={{ textAlign: 'left', padding: '6px 8px' }}>Email</th>
<th style={{ textAlign: 'left', padding: '6px 8px' }}>Email (login)</th>
<th style={{ textAlign: 'left', padding: '6px 8px' }}>Status</th>
<th style={{ padding: '6px 8px' }}></th>
</tr>
@@ -1222,8 +1219,7 @@ function NuggetsUploadPortalAdmin() {
{hosts.map(h => (
<tr key={h.id} style={{ borderBottom: '1px solid var(--color-border, #e5e0d5)' }}>
<td style={{ padding: '7px 8px' }}>{h.name}</td>
<td style={{ padding: '7px 8px', fontFamily: 'monospace' }}>{h.username}</td>
<td style={{ padding: '7px 8px', fontSize: '0.82rem', color: '#888' }}>{h.email || <em>none</em>}</td>
<td style={{ padding: '7px 8px', fontSize: '0.85rem' }}>{h.email}</td>
<td style={{ padding: '7px 8px' }}>
<span style={{ color: h.active ? '#27ae60' : '#888', fontWeight: 600 }}>{h.active ? 'Active' : 'Disabled'}</span>
</td>
@@ -1266,13 +1262,9 @@ function NuggetsUploadPortalAdmin() {
<label>Display Name</label>
<input type="text" placeholder="Jane Smith" value={addName} onChange={e => setAddName(e.target.value)} required />
</div>
<div className="admin-field" style={{ flex: '1 1 130px' }}>
<label>Username</label>
<input type="text" placeholder="janesmith" value={addUsername} onChange={e => setAddUsername(e.target.value.toLowerCase())} required />
</div>
<div className="admin-field" style={{ flex: '1 1 180px' }}>
<label>Email (optional — sends welcome)</label>
<input type="email" placeholder="jane@example.com" value={addEmail} onChange={e => setAddEmail(e.target.value)} />
<div className="admin-field" style={{ flex: '1 1 200px' }}>
<label>Email — used to log in</label>
<input type="email" placeholder="jane@example.com" value={addEmail} onChange={e => setAddEmail(e.target.value)} required />
</div>
<div className="admin-field" style={{ flex: '1 1 150px' }}>
<label>Password (min 8 chars)</label>