Add client-side note encryption
- WebCrypto keyring (PBKDF2 600k + AES-GCM master key) and per-note/per-image keys - Encrypt/decrypt commands convert the note and its own images, deleting the originals - Unlock, setup and change-password dialogs; manual lock; lock on page close - Encrypted images are decrypted to blob URLs; pasted images are encrypted before upload - Rewrite image links in the browser when renaming an encrypted note - Skip local drafts for encrypted notes and confirm before closing with unsaved changes - Show encrypted notes as locked with an explanation outside secure contexts
This commit is contained in:
1 parent
35d3138c7d
commit
5cf5a45682
20 files changed
+899
-39
No files matched your search
@@ -3,7 +3,8 @@ import { EditorState, type StateEffect, type Text } from '@codemirror/state'
|
||||
import type { EditorView } from '@codemirror/view'
|
||||
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||
import { createEditorView } from '../editor'
|
||||
import { activeTab, attachView, editorStateOf, noteRefOf, revealPending, storeViewState, ws } from './workspace'
|
||||
import { keyState } from './encryption'
|
||||
import { activeTab, attachView, editorStateOf, noteRefOf, revealPending, storeViewState, unlockNotes, ws } from './workspace'
|
||||
|
||||
const host = ref<HTMLElement>()
|
||||
const tab = computed(activeTab)
|
||||
@@ -28,6 +29,8 @@ function show() {
|
||||
scrollPositions.set(shown, { doc: view.state.doc, snapshot: view.scrollSnapshot(), top: view.scrollDOM.scrollTop })
|
||||
}
|
||||
if (!ref || !state) {
|
||||
// 锁定后视图中不能留下已解密的内容
|
||||
if (shown) view.setState(EditorState.create())
|
||||
shown = undefined
|
||||
return
|
||||
}
|
||||
@@ -60,10 +63,17 @@ watch(() => [ws.active, tab.value?.status], show, { flush: 'post' })
|
||||
|
||||
<template>
|
||||
<div class="rm-editor-pane">
|
||||
<div v-show="tab && tab.status !== 'loading' && tab.status !== 'error'" ref="host" class="rm-editor-host" />
|
||||
<div v-show="tab && !['loading', 'error', 'locked'].includes(tab.status)" ref="host" class="rm-editor-host" />
|
||||
<div v-if="!tab" class="rm-empty">
|
||||
<p>选择或新建一篇笔记</p>
|
||||
</div>
|
||||
<div v-else-if="tab.status === 'error'" class="rm-empty"><p>笔记打开失败</p></div>
|
||||
<div v-else-if="tab.status === 'locked'" class="rm-empty">
|
||||
<template v-if="keyState.supported">
|
||||
<p>这是一篇加密笔记</p>
|
||||
<button class="rm-btn rm-btn-primary" @click="unlockNotes()">解锁</button>
|
||||
</template>
|
||||
<p v-else class="rm-locked-hint">这是一篇加密笔记。当前连接不是 HTTPS,浏览器不允许网页使用加密功能,无法查看。请通过 HTTPS 访问。</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in new issue
Block a user