First-run creates the superadmin in-app; wizard seeds reference data

No CLI needed for a UI-driven install.

- Login page: when the instance has no users yet (/setup/needs-admin), it shows
  a "create the first admin" form instead of login. Submitting creates the admin
  (admin role = full access / superadmin) via /setup/create-admin, logs in, and
  goes to the setup wizard.
- Wizard Starter Data step: "Seed core reference data" button (statuses, types,
  permissions, default settings via /setup/seed-reference) alongside the common
  vendors, so a UI-first install gets the data the app needs.
- Add waitress to requirements.txt (the IIS web.config launches it but it was
  missing - found while validating the Windows install).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-07-10 11:53:38 -04:00
parent 301eaa6375
commit 57efb5f965
3 changed files with 90 additions and 32 deletions

View File

@@ -112,10 +112,21 @@
<!-- Starter data -->
<div v-else-if="current.key === 'data'">
<h2>Starter data</h2>
<p class="hint">Add common hardware vendors so you can start adding assets right away. Safe to run - it skips ones you already have.</p>
<button class="btn btn-secondary" :disabled="seeding" @click="seedStarter">
{{ seeding ? 'Adding...' : 'Add common vendors' }}
</button>
<p class="hint">Seed the data a new site needs. Both are idempotent - safe to run more than once.</p>
<div class="starter-actions">
<div>
<button class="btn btn-secondary" :disabled="seeding" @click="seedReference">
{{ seeding ? 'Working...' : 'Seed core reference data' }}
</button>
<p class="hint">Statuses, machine/location/relationship types, permissions, default settings. Run this if you installed without the CLI seed.</p>
</div>
<div>
<button class="btn btn-secondary" :disabled="seeding" @click="seedStarter">
{{ seeding ? 'Working...' : 'Add common vendors' }}
</button>
<p class="hint">Dell, HP, Lenovo, and other common hardware vendors.</p>
</div>
</div>
<p v-if="seedResult" class="seed-result">{{ seedResult }}</p>
</div>
@@ -325,6 +336,18 @@ async function togglePlugin(plugin, enabled) {
}
}
async function seedReference() {
seeding.value = true
try {
const response = await setupApi.seedReference()
seedResult.value = response.data?.message || 'Reference data seeded.'
} catch (err) {
toast.error(apiError(err, 'Could not seed reference data'))
} finally {
seeding.value = false
}
}
async function seedStarter() {
seeding.value = true
try {
@@ -384,6 +407,8 @@ async function finish() {
.provision-note p:last-child { margin-bottom: 0; }
.provision-tables code, .provision-docs code { background: var(--bg-card); border: 1px solid var(--border); border-radius: 3px; padding: 0 0.3rem; margin-right: 0.3rem; font-size: 0.78rem; }
.seed-result { margin-top: 0.75rem; color: var(--success); font-size: 0.88rem; }
.starter-actions { display: flex; flex-direction: column; gap: 1.1rem; }
.starter-actions .hint { margin: 0.35rem 0 0; }
.config-block { margin-top: 1.25rem; padding-top: 1rem; border-top: 1px solid var(--border); }
.config-title { margin: 0 0 0.75rem; font-size: 1rem; text-transform: capitalize; }
.secret-tag { margin-left: 0.5rem; font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--warning); border: 1px solid var(--warning); border-radius: 4px; padding: 0 0.3rem; }