feat(vault): initialize fresh instances with public example notes

This commit is contained in:
eezd committed 2026-10-27 13:41:10 +08:00
1 parent 9ee7402c8e
commit 10fd6d8efb
24 files changed
+438 -14

No files matched your search

+34
View File
@@ -0,0 +1,34 @@
package main
import (
"fmt"
"io/fs"
"rawmark/deploy"
"rawmark/internal/auth"
"rawmark/internal/store"
"rawmark/internal/vault"
)
// Keep bootstrap state out of user preferences so resetting settings cannot restore deleted examples.
const examplesSetting = "bootstrap.examples"
func initializeExamples(v *vault.Vault, st *store.Store) error {
_, done, err := st.Setting(examplesSetting)
if err != nil || done {
return err
}
setUp, err := auth.New(st).IsSetUp()
if err != nil {
return err
}
if !setUp {
examples, err := fs.Sub(deploy.Examples, "data/vault")
if err != nil {
return err
}
if err := v.SeedExamples(examples); err != nil {
return fmt.Errorf("初始化示例笔记失败: %w", err)
}
}
return st.SetSetting(examplesSetting, "1")
}
+109
View File
@@ -0,0 +1,109 @@
package main
import (
"io/fs"
"os"
"path/filepath"
"rawmark/deploy"
"rawmark/internal/auth"
"rawmark/internal/store"
"rawmark/internal/vault"
"testing"
)
func TestInitializeExamplesOnlyOnce(t *testing.T) {
data := t.TempDir()
v, err := vault.Open(filepath.Join(data, "vault"))
if err != nil {
t.Fatal(err)
}
st, err := store.Open(filepath.Join(data, "rawmark.db"))
if err != nil {
t.Fatal(err)
}
if err := initializeExamples(v, st); err != nil {
t.Fatal(err)
}
if ready, err := auth.New(st).IsSetUp(); err != nil || ready {
t.Fatalf("examples must not initialize credentials: %v, %v", ready, err)
}
count := 0
if err := fs.WalkDir(deploy.Examples, "data/vault", func(name string, entry fs.DirEntry, walkErr error) error {
if walkErr != nil {
return walkErr
}
if entry.IsDir() {
return nil
}
if filepath.Ext(name) != ".md" {
t.Fatalf("unexpected template file: %s", name)
}
count++
rel, err := filepath.Rel("data/vault", name)
if err != nil {
return err
}
if _, err := os.Stat(filepath.Join(v.Root(), rel)); err != nil {
return err
}
return nil
}); err != nil {
t.Fatal(err)
}
if count != 7 {
t.Fatalf("expected seven public sample notes, got %d", count)
}
// Deletion before password setup, settings reset, and process restart must not restore examples.
entries, err := os.ReadDir(v.Root())
if err != nil {
t.Fatal(err)
}
for _, entry := range entries {
if entry.Name() != ".rawmark" {
if err := os.RemoveAll(filepath.Join(v.Root(), entry.Name())); err != nil {
t.Fatal(err)
}
}
}
if err := st.ResetPreferences(); err != nil {
t.Fatal(err)
}
if err := st.Close(); err != nil {
t.Fatal(err)
}
st, err = store.Open(filepath.Join(data, "rawmark.db"))
if err != nil {
t.Fatal(err)
}
defer st.Close()
if err := initializeExamples(v, st); err != nil {
t.Fatal(err)
}
entries, err = os.ReadDir(v.Root())
if err != nil || len(entries) != 1 || entries[0].Name() != ".rawmark" {
t.Fatalf("deleted examples were restored: %v, %v", entries, err)
}
}
func TestInitializeExamplesSkipsInitializedEmptyInstance(t *testing.T) {
data := t.TempDir()
v, err := vault.Open(filepath.Join(data, "vault"))
if err != nil {
t.Fatal(err)
}
st, err := store.Open(filepath.Join(data, "rawmark.db"))
if err != nil {
t.Fatal(err)
}
defer st.Close()
if err := auth.New(st).Setup("synthetic-test-password"); err != nil {
t.Fatal(err)
}
if err := initializeExamples(v, st); err != nil {
t.Fatal(err)
}
entries, err := os.ReadDir(v.Root())
if err != nil || len(entries) != 1 || entries[0].Name() != ".rawmark" {
t.Fatalf("examples were added to an existing account: %v, %v", entries, err)
}
}
+3
View File
@@ -129,6 +129,9 @@ func serve(parent context.Context, addr, dataDir, vaultDir, version string, font
return false, err
}
defer st.Close()
if err := initializeExamples(v, st); err != nil {
return false, err
}
idx, err := search.Open(filepath.Join(dataDir, "index.db"), v)
if err != nil {
return false, err