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>
This commit is contained in:
134
frontend/src/components/wizard/SetupWizard.vue
Normal file
134
frontend/src/components/wizard/SetupWizard.vue
Normal file
@@ -0,0 +1,134 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user