valaxy 0.14.45 → 0.14.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,98 @@
1
+ <script lang="ts" setup>
2
+ import { useDecrypt, useFrontmatter } from 'valaxy'
3
+ import type { Ref } from 'vue'
4
+ import { defineComponent, h, inject, ref } from 'vue'
5
+
6
+ const props = defineProps<{
7
+ encryptedContent: string
8
+ }>()
9
+
10
+ const password = ref('')
11
+ const decryptedContent = ref('')
12
+
13
+ const hasError = ref(false)
14
+
15
+ const onContentUpdated = inject('onContentUpdated') as Ref<() => void>
16
+
17
+ const { decrypt } = useDecrypt()
18
+ async function decryptContent() {
19
+ const ciphertext = props.encryptedContent
20
+ if (!ciphertext)
21
+ return
22
+ try {
23
+ const result = await decrypt(password.value, ciphertext)
24
+ decryptedContent.value = result || ''
25
+
26
+ setTimeout(() => {
27
+ onContentUpdated.value?.()
28
+ }, 16)
29
+ }
30
+ catch (e) {
31
+ hasError.value = true
32
+ }
33
+ }
34
+
35
+ function encryptAgain() {
36
+ decryptedContent.value = ''
37
+ password.value = ''
38
+
39
+ setTimeout(() => {
40
+ onContentUpdated.value?.()
41
+ }, 16)
42
+ }
43
+
44
+ const ValaxyDeprecatedContent = defineComponent({
45
+ name: 'ValaxyDeprecatedContent',
46
+ props: {
47
+ html: String,
48
+ },
49
+ render() {
50
+ const content = `<div>${this.html}</div>`
51
+ return h({
52
+ setup: () => {
53
+ const fm = useFrontmatter()
54
+ return {
55
+ frontmatter: fm,
56
+ }
57
+ },
58
+ template: content,
59
+ })
60
+ },
61
+ })
62
+ </script>
63
+
64
+ <template>
65
+ <div>
66
+ <div v-if="!decryptedContent" w-full pt-14 pb-10>
67
+ <div
68
+ class="decrypt-password-container w-full sm:w-1/2"
69
+ flex-center m-auto relative
70
+ >
71
+ <input
72
+ v-model="password"
73
+ w-full
74
+ border pl-5 pr-11 py-3 rounded hover:shadow transition
75
+ type="password" placeholder="Enter password"
76
+ :class="hasError && 'border-red'"
77
+ @input="hasError = false"
78
+ @keyup.enter="decryptContent"
79
+ >
80
+ <div
81
+ cursor-pointer
82
+ absolute text-2xl
83
+ i-ri-arrow-right-circle-line right-3
84
+ text-gray hover:text-black
85
+ @click="decryptContent"
86
+ />
87
+ </div>
88
+ </div>
89
+ <div v-else>
90
+ <ValaxyDeprecatedContent :html="decryptedContent" />
91
+ <div w-full text-center mt-8>
92
+ <button m-auto class="btn" font-bold @click="encryptAgain">
93
+ Encrypt Again
94
+ </button>
95
+ </div>
96
+ </div>
97
+ </div>
98
+ </template>
@@ -14,9 +14,9 @@ const onContentUpdated = inject('onContentUpdated') as Ref<() => void>
14
14
 
15
15
  const { t } = useI18n()
16
16
 
17
- const content = ref()
17
+ const contentRef = ref()
18
18
  function updateDom() {
19
- wrapTable(content.value)
19
+ wrapTable(contentRef.value)
20
20
  onContentUpdated.value?.()
21
21
  }
22
22
 
@@ -39,11 +39,14 @@ if (typeof props.frontmatter.medium_zoom === 'undefined' || props.frontmatter.me
39
39
 
40
40
  <template>
41
41
  <article v-if="$slots.default" :class="frontmatter.markdown !== false && 'markdown-body'">
42
- <slot ref="content" @vue:updated="updateDom" />
42
+ <template v-if="frontmatter.encryptedContent">
43
+ <ValaxyDecrypt :encrypted-content="frontmatter.encryptedContent" />
44
+ </template>
45
+ <slot v-else ref="contentRef" @vue:updated="updateDom" />
43
46
 
44
- <div text="center">
47
+ <div v-if="frontmatter.url" text="center">
45
48
  <a
46
- v-if="frontmatter.url"
49
+
47
50
  :href="frontmatter.url"
48
51
  class="post-link-btn shadow hover:shadow-md"
49
52
  rounded
@@ -0,0 +1,66 @@
1
+ import { useSiteConfig } from 'valaxy'
2
+
3
+ /**
4
+ * @see https://developer.mozilla.org/zh-CN/docs/Web/API/SubtleCrypto/deriveKey#pbkdf2_2
5
+ * @param password
6
+ * @returns
7
+ */
8
+ export function getKeyMaterial(password: string) {
9
+ const enc = new TextEncoder()
10
+ return window.crypto.subtle.importKey(
11
+ 'raw',
12
+ enc.encode(password),
13
+ 'PBKDF2',
14
+ false,
15
+ ['deriveBits', 'deriveKey'],
16
+ )
17
+ }
18
+
19
+ export function getKey(keyMaterial: CryptoKey, salt: Uint8Array) {
20
+ return window.crypto.subtle.deriveKey(
21
+ {
22
+ name: 'PBKDF2',
23
+ salt,
24
+ iterations: 100000,
25
+ hash: 'SHA-256',
26
+ },
27
+ keyMaterial,
28
+ {
29
+ name: 'AES-CBC',
30
+ length: 256,
31
+ },
32
+ true,
33
+ ['encrypt', 'decrypt'],
34
+ )
35
+ }
36
+
37
+ export function useDecrypt() {
38
+ const siteConfig = useSiteConfig()
39
+
40
+ const { encrypt } = siteConfig.value
41
+ const iv = Uint8Array.from(Object.values(encrypt.iv))
42
+ const salt = Uint8Array.from(Object.values(encrypt.salt))
43
+
44
+ return {
45
+ decrypt: async (password: string, ciphertext: string) => {
46
+ if (!password)
47
+ return
48
+
49
+ const keyMaterial = await getKeyMaterial(password)
50
+ const key = await getKey(keyMaterial, salt)
51
+
52
+ const ciphertextData = Uint8Array.from(ciphertext, c => c.charCodeAt(0))
53
+
54
+ const decrypted = await window.crypto.subtle.decrypt(
55
+ {
56
+ name: 'AES-CBC',
57
+ iv,
58
+ },
59
+ key,
60
+ ciphertextData,
61
+ )
62
+
63
+ return new TextDecoder().decode(decrypted)
64
+ },
65
+ }
66
+ }
@@ -15,3 +15,6 @@ export * from './locale'
15
15
  export * from './sidebar'
16
16
  export * from './outline'
17
17
  export * from './body-scroll-lock'
18
+
19
+ // utils
20
+ export * from './decrypt'
package/client/main.ts CHANGED
@@ -2,6 +2,7 @@ import { ViteSSG } from 'vite-ssg'
2
2
  import generatedRoutes from 'virtual:generated-pages'
3
3
  import { setupLayouts } from 'virtual:generated-layouts'
4
4
  import App from './App.vue'
5
+ import AppLinkVue from './components/AppLink.vue'
5
6
 
6
7
  import '@unocss/reset/tailwind.css'
7
8
 
@@ -31,6 +32,7 @@ export const createApp = ViteSSG(
31
32
  },
32
33
  },
33
34
  (ctx) => {
35
+ ctx.app.component('AppLink', AppLinkVue)
34
36
  setupMain(ctx)
35
37
  },
36
38
  )