Files
Archivum/frontend/src/components/wizard/SetupWizard.vue
brasse b e6b64e3344 feat: Archivum skeleton — Go/GraphQL backend + Vue 3 PWA frontend
Full project scaffold: multi-stage Dockerfile (ARM64/AMD64), AsciiDoc↔TipTap
bridge, Setup Wizard, CodeMirror source editor, Git-backed storage layer,
LDAP+JWT auth skeleton, Tailwind mobile-first layout, and VS Code build/push
tasks targeting registry at 192.168.0.19:5000.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 13:07:01 +02:00

135 lines
4.4 KiB
Vue

<script setup lang="ts">
import { reactive, ref } from 'vue'
import { useRouter } from 'vue-router'
import { gql } from '@/lib/gql'
import { useAppStore } from '@/stores/app'
const router = useRouter()
const app = useAppStore()
const step = ref(1)
const error = ref('')
const submitting = ref(false)
const form = reactive({
storagePath: '/data/wiki',
adminUser: 'admin',
adminPass: '',
jwtSecret: crypto.randomUUID().replace(/-/g, ''),
ldapEnabled: false,
ldap: {
host: '',
port: 389,
baseDN: '',
bindDN: '',
bindPassword: '',
},
})
async function submit() {
error.value = ''
submitting.value = true
try {
await gql(
`mutation Setup($i: SetupInput!) { setup(input: $i) }`,
{
i: {
storagePath: form.storagePath,
adminUser: form.adminUser,
adminPass: form.adminPass,
jwtSecret: form.jwtSecret,
ldap: form.ldapEnabled ? form.ldap : null,
},
},
)
app.requiresSetup = false
await app.login(form.adminUser, form.adminPass)
router.replace('/')
} catch (e: unknown) {
error.value = e instanceof Error ? e.message : String(e)
} finally {
submitting.value = false
}
}
</script>
<template>
<div class="min-h-screen flex items-center justify-center bg-surface-900 px-4">
<div class="w-full max-w-md bg-surface-800 rounded-2xl p-8 shadow-xl">
<h1 class="text-2xl font-bold mb-1">Welcome to Archivum</h1>
<p class="text-slate-400 text-sm mb-6">Complete setup to get started.</p>
<!-- Step 1: Storage & Admin -->
<form v-if="step === 1" class="space-y-4" @submit.prevent="step = 2">
<div>
<label class="block text-sm mb-1">Storage path</label>
<input v-model="form.storagePath" required class="input" />
</div>
<div>
<label class="block text-sm mb-1">Admin username</label>
<input v-model="form.adminUser" required class="input" />
</div>
<div>
<label class="block text-sm mb-1">Admin password</label>
<input v-model="form.adminPass" type="password" required class="input" />
</div>
<button type="submit" class="btn-primary w-full">Next </button>
</form>
<!-- Step 2: LDAP (optional) -->
<form v-else-if="step === 2" class="space-y-4" @submit.prevent="submit">
<label class="flex items-center gap-2 cursor-pointer">
<input v-model="form.ldapEnabled" type="checkbox" class="rounded" />
<span class="text-sm">Enable LDAP authentication</span>
</label>
<template v-if="form.ldapEnabled">
<div class="grid grid-cols-2 gap-3">
<div>
<label class="block text-sm mb-1">Host</label>
<input v-model="form.ldap.host" required class="input" />
</div>
<div>
<label class="block text-sm mb-1">Port</label>
<input v-model.number="form.ldap.port" type="number" required class="input" />
</div>
</div>
<div>
<label class="block text-sm mb-1">Base DN</label>
<input v-model="form.ldap.baseDN" required class="input" />
</div>
<div>
<label class="block text-sm mb-1">Bind DN</label>
<input v-model="form.ldap.bindDN" required class="input" />
</div>
<div>
<label class="block text-sm mb-1">Bind password</label>
<input v-model="form.ldap.bindPassword" type="password" required class="input" />
</div>
</template>
<p v-if="error" class="text-red-400 text-sm">{{ error }}</p>
<div class="flex gap-3">
<button type="button" class="btn-secondary flex-1" @click="step = 1"> Back</button>
<button type="submit" class="btn-primary flex-1" :disabled="submitting">
{{ submitting ? 'Setting up…' : 'Finish setup' }}
</button>
</div>
</form>
</div>
</div>
</template>
<style scoped>
.input {
@apply w-full bg-surface-900 border border-slate-600 rounded-lg px-3 py-2 text-sm focus:outline-none focus:border-blue-500;
}
.btn-primary {
@apply py-2 rounded-lg bg-blue-600 hover:bg-blue-500 font-medium text-sm disabled:opacity-50 transition;
}
.btn-secondary {
@apply py-2 rounded-lg bg-surface-900 hover:bg-surface-700 font-medium text-sm transition;
}
</style>