|
| 1 | +<script setup lang="ts"> |
| 2 | +import { onMounted, ref } from 'vue' |
| 3 | +import { LazyHydrationWrapper } from 'vue3-lazy-hydration' |
| 4 | +import { decryptText, encryptText } from '../composables/crypto' |
| 5 | +
|
| 6 | +const password = ref('') |
| 7 | +const iv = ref('') |
| 8 | +const errored = ref(false) |
| 9 | +
|
| 10 | +const rawContent = '<div>a</div>' |
| 11 | +const encryptedHtml = ref('') |
| 12 | +const decryptedHtml = ref('') |
| 13 | +const trigger = ref(false) |
| 14 | +
|
| 15 | +onMounted(async () => { |
| 16 | + try { |
| 17 | + const key = 'password' |
| 18 | + const encrypted = await encryptText(rawContent, key) |
| 19 | + encryptedHtml.value = encrypted.encryptedText |
| 20 | + iv.value = encrypted.ivBase64 |
| 21 | + } |
| 22 | + catch (err) { |
| 23 | + console.error('Failed to encrypt the content.') |
| 24 | + } |
| 25 | +}) |
| 26 | +
|
| 27 | +async function onPasswordInput() { |
| 28 | + try { |
| 29 | + decryptedHtml.value = await decryptText(encryptedHtml.value, password.value, iv.value) |
| 30 | + trigger.value = true |
| 31 | + } |
| 32 | + catch (err) { |
| 33 | + console.error('Invalid password.') |
| 34 | + errored.value = true |
| 35 | + return |
| 36 | + } |
| 37 | +
|
| 38 | + errored.value = false |
| 39 | +} |
| 40 | +</script> |
| 41 | + |
| 42 | +<template> |
| 43 | + <div id="vp-nolebase-protected"> |
| 44 | + <div> |
| 45 | + <div relative> |
| 46 | + <div flex="~ col" absolute left-0 top-0 z-10 h-full w-full items-center justify-center> |
| 47 | + <div mb-4> |
| 48 | + <p mt="0!" mb="4!" text-center> |
| 49 | + <span font-semibold>You don't have permissions to access this page.</span> |
| 50 | + </p> |
| 51 | + <p my="0!" text-left> |
| 52 | + <span>You could either</span> |
| 53 | + <ul my="0!"> |
| 54 | + <li my="0!"> |
| 55 | + Request an access permission from the owner. |
| 56 | + </li> |
| 57 | + <li my="0!"> |
| 58 | + Prompt a valid password for it. |
| 59 | + </li> |
| 60 | + </ul> |
| 61 | + </p> |
| 62 | + </div> |
| 63 | + <div w-full flex="~ col" items-center justify-center max-w="80"> |
| 64 | + <button |
| 65 | + min-w-35 w-full rounded-lg |
| 66 | + px-3 py-2 |
| 67 | + bg="zinc-700 hover:zinc-600 active:zinc-700 dark:zinc-200 dark:hover:zinc-300 dark:active:zinc-400" |
| 68 | + text="zinc-100 dark:zinc-900 " font-semibold |
| 69 | + transition="all ease" duration-750 |
| 70 | + > |
| 71 | + Request Access |
| 72 | + </button> |
| 73 | + <span>or</span> |
| 74 | + <form min-w-35 w-full flex="~ row" @submit.prevent="() => {}"> |
| 75 | + <input |
| 76 | + v-model="password" |
| 77 | + type="password" |
| 78 | + mr-2 |
| 79 | + w-full rounded-lg |
| 80 | + px-3 py-2 |
| 81 | + bg="$vp-c-bg" |
| 82 | + text="$vp-c-text-1" font-semibold |
| 83 | + transition="all ease" duration-750 |
| 84 | + :class="[ |
| 85 | + errored ? 'outline-offset-1 outline-2 outline-red-400' : 'outline-offset-1 outline-2 outline-zinc-100', |
| 86 | + ]" |
| 87 | + placeholder="Enter the valid password..." |
| 88 | + > |
| 89 | + <button |
| 90 | + rounded-lg |
| 91 | + px-3 py-2 |
| 92 | + font-semibold |
| 93 | + transition="all ease" duration-750 |
| 94 | + :class="[ |
| 95 | + password !== '' ? 'bg-zinc-700 hover:bg-zinc-600 active:bg-zinc-700 dark:bg-zinc-200 dark:hover:bg-zinc-300 dark:active:bg-zinc-400 text-zinc-100 dark:text-zinc-900' : 'bg-$vp-c-bg cursor-not-allowed! text-$vp-c-text-1', |
| 96 | + ]" |
| 97 | + @click="onPasswordInput" |
| 98 | + > |
| 99 | + Unlock |
| 100 | + </button> |
| 101 | + </form> |
| 102 | + </div> |
| 103 | + </div> |
| 104 | + <div blur-md space-y-5> |
| 105 | + <div h="[1lh]" bg="[var(--vp-c-text-1)]" w-full rounded-lg opacity-5 /> |
| 106 | + <div h="[1lh]" bg="[var(--vp-c-text-1)]" w-full rounded-lg opacity-5 /> |
| 107 | + <div h="[1lh]" bg="[var(--vp-c-text-1)]" w-full rounded-lg opacity-5 /> |
| 108 | + <div h="[1lh]" bg="[var(--vp-c-text-1)]" w-full rounded-lg opacity-5 /> |
| 109 | + <div h="[1lh]" bg="[var(--vp-c-text-1)]" w-full rounded-lg opacity-5 /> |
| 110 | + <div h="[1lh]" bg="[var(--vp-c-text-1)]" w-full rounded-lg opacity-5 /> |
| 111 | + <div h="[1lh]" bg="[var(--vp-c-text-1)]" w-full rounded-lg opacity-5 /> |
| 112 | + <div h="[1lh]" bg="[var(--vp-c-text-1)]" w-full rounded-lg opacity-5 /> |
| 113 | + <div h="[1lh]" bg="[var(--vp-c-text-1)]" w-full rounded-lg opacity-5 /> |
| 114 | + <div h="[1lh]" bg="[var(--vp-c-text-1)]" w-full rounded-lg opacity-5 /> |
| 115 | + <div h="[1lh]" bg="[var(--vp-c-text-1)]" w-full rounded-lg opacity-5 /> |
| 116 | + </div> |
| 117 | + </div> |
| 118 | + <LazyHydrationWrapper :when-triggered="trigger"> |
| 119 | + <div id="vp-nolebase-protected-content"> |
| 120 | + <slot /> |
| 121 | + </div> |
| 122 | + <div v-html="decryptedHtml" /> |
| 123 | + </LazyHydrationWrapper> |
| 124 | + </div> |
| 125 | + </div> |
| 126 | +</template> |
0 commit comments