2026-03-02 14:15:39 +00:00
|
|
|
<template>
|
2026-03-02 16:34:44 +00:00
|
|
|
<div class="h-dvh flex flex-col" :class="currentTheme">
|
2026-03-02 14:15:39 +00:00
|
|
|
<RouterView />
|
2026-03-02 21:29:50 +00:00
|
|
|
<ArticleOverlay />
|
2026-03-03 20:52:53 +00:00
|
|
|
<PassphraseDialog
|
|
|
|
|
:visible="showPassphrase"
|
|
|
|
|
:is-creating="isCreatingPassphrase"
|
|
|
|
|
@submit="handlePassphraseSubmit"
|
|
|
|
|
@skip="showPassphrase = false"
|
|
|
|
|
/>
|
2026-03-02 14:15:39 +00:00
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2026-03-03 20:52:53 +00:00
|
|
|
import { ref, onMounted } from 'vue'
|
2026-03-02 14:15:39 +00:00
|
|
|
import { RouterView } from 'vue-router'
|
2026-03-02 16:34:44 +00:00
|
|
|
import { useTheme } from '@/composables/useTheme'
|
2026-03-02 21:29:50 +00:00
|
|
|
import ArticleOverlay from '@/components/content/ArticleOverlay.vue'
|
2026-03-03 20:52:53 +00:00
|
|
|
import PassphraseDialog from '@/components/ui/PassphraseDialog.vue'
|
|
|
|
|
import {
|
|
|
|
|
isCryptoEnabled,
|
|
|
|
|
deriveKey,
|
|
|
|
|
generateSalt,
|
|
|
|
|
setSessionKey,
|
|
|
|
|
} from '@/utils/crypto'
|
2026-03-02 16:34:44 +00:00
|
|
|
|
|
|
|
|
const { currentTheme, initTheme } = useTheme()
|
|
|
|
|
|
2026-03-03 20:52:53 +00:00
|
|
|
const showPassphrase = ref(false)
|
|
|
|
|
const isCreatingPassphrase = ref(false)
|
|
|
|
|
|
|
|
|
|
const SALT_KEY = 'aiui-crypto-salt'
|
|
|
|
|
|
|
|
|
|
async function handlePassphraseSubmit(passphrase: string) {
|
|
|
|
|
let saltHex = localStorage.getItem(SALT_KEY)
|
|
|
|
|
let salt: Uint8Array
|
|
|
|
|
|
|
|
|
|
if (saltHex) {
|
|
|
|
|
salt = new Uint8Array(saltHex.match(/.{2}/g)!.map(b => parseInt(b, 16)))
|
|
|
|
|
} else {
|
|
|
|
|
salt = await generateSalt()
|
|
|
|
|
saltHex = Array.from(salt).map(b => b.toString(16).padStart(2, '0')).join('')
|
|
|
|
|
localStorage.setItem(SALT_KEY, saltHex)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const key = await deriveKey(passphrase, salt)
|
|
|
|
|
setSessionKey(key, salt)
|
|
|
|
|
showPassphrase.value = false
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-02 16:34:44 +00:00
|
|
|
onMounted(() => {
|
|
|
|
|
initTheme()
|
2026-03-03 20:52:53 +00:00
|
|
|
if (isCryptoEnabled()) {
|
|
|
|
|
const hasSalt = !!localStorage.getItem(SALT_KEY)
|
|
|
|
|
isCreatingPassphrase.value = !hasSalt
|
|
|
|
|
showPassphrase.value = true
|
|
|
|
|
}
|
2026-03-02 16:34:44 +00:00
|
|
|
})
|
2026-03-02 14:15:39 +00:00
|
|
|
</script>
|