Add encrypted note storage and keyring endpoints

- Treat name.md.enc as encrypted notes; forbid coexisting twins sharing assets
- Convert between plain and encrypted notes via PUT ?convertFrom
- Store keyring at .rawmark/keyring.json with versioned writes
- Allow permanent deletion of single asset files
- Exclude encrypted notes from the search index
This commit is contained in:
eezd committed 2026-10-17 14:24:55 +08:00
1 parent 75000fc571
commit 35d3138c7d
5 files changed
+329 -32

No files matched your search

+19 -9
View File
@@ -92,15 +92,15 @@ func TestSearchTerms(t *testing.T) {
f.sync()
cases := map[string]string{
"装饰器": "a.md,目录/c.md", // 3 个字符以上走 FTS
"检索": "b.md,目录/c.md", // 不足 3 个字符走 LIKE
"codemirror": "a.md", // 不区分大小写
"装饰器 检索": "目录/c.md", // 多个词同时满足
"目录": "目录/c.md", // 命中路径
"不存在的词": "",
"100%": "",
`"引号"`: "",
" ": "",
"装饰器": "a.md,目录/c.md", // 3 个字符以上走 FTS
"检索": "b.md,目录/c.md", // 不足 3 个字符走 LIKE
"codemirror": "a.md", // 不区分大小写
"装饰器 检索": "目录/c.md", // 多个词同时满足
"目录": "目录/c.md", // 命中路径
"不存在的词": "",
"100%": "",
`"引号"`: "",
" ": "",
}
for q, want := range cases {
if got := f.paths(q); got != want && !(strings.Contains(want, ",") && sameSet(got, want)) {
@@ -179,3 +179,13 @@ func TestSyncTracksChanges(t *testing.T) {
t.Errorf("重启后搜索 = %q", got)
}
}
func TestEncryptedNotesNotIndexed(t *testing.T) {
f := newFixture(t)
f.write("明文.md", "公开内容")
f.write("秘密.md.enc", `{"data":"公开内容"}`)
f.sync()
if got := f.paths("公开内容"); got != "明文.md" {
t.Errorf("加密笔记不应入索引: %q", got)
}
}