feat(server): add Go backend with vault, auth, events and REST API
- vault: path validation (no traversal, hidden segments or symlink escape), atomic writes with If-Match/If-None-Match revisions, moves that carry the note's .assets folder and rewrite its image links, trash with restore, content-addressed asset uploads - auth: single-user Argon2id password, sliding sessions in SQLite, login rate limiting, first-run setup gated by a one-time code written to the data directory - watch: fsnotify plus periodic rescans diffed against a snapshot, so both RawMark and external edits are broadcast as tree/changed/moved/ deleted events over WebSocket - api: stdlib router, SameSite=Strict cookies plus Origin checks, sandboxed raw file serving, embedded SPA with gzip and CSP
This commit is contained in:
1 parent
5746f2e9ae
commit
c4d2bd4636
15 files changed
+2196
No files matched your search
@@ -0,0 +1,48 @@
|
||||
package watch
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDiff(t *testing.T) {
|
||||
prev := map[string]fileState{
|
||||
"d": {dir: true},
|
||||
"d/a.md": {rev: "r1"},
|
||||
"b.md": {rev: "r2"},
|
||||
"c.md": {rev: "same"},
|
||||
"c2.md": {rev: "same"},
|
||||
}
|
||||
next := map[string]fileState{
|
||||
"d": {dir: true},
|
||||
"d/x.md": {rev: "r1"}, // 由 d/a.md 移动而来
|
||||
"b.md": {rev: "r3"}, // 内容修改
|
||||
"e.md": {rev: "same"}, // c.md 与 c2.md 内容相同:其一视为移动到此,另一个为删除
|
||||
}
|
||||
var got []string
|
||||
for _, e := range diff(prev, next) {
|
||||
f := []string{e.Type}
|
||||
if e.From != "" {
|
||||
f = append(f, "c*")
|
||||
if !strings.HasPrefix(e.From, "c") {
|
||||
f[1] = e.From
|
||||
}
|
||||
}
|
||||
if e.Path != "" {
|
||||
f = append(f, e.Path)
|
||||
if e.Type == "deleted" && strings.HasPrefix(e.Path, "c") {
|
||||
f[1] = "c*"
|
||||
}
|
||||
}
|
||||
got = append(got, strings.Join(f, " "))
|
||||
}
|
||||
sort.Strings(got)
|
||||
want := "changed b.md|deleted c*|moved c* e.md|moved d/a.md d/x.md|tree"
|
||||
if strings.Join(got, "|") != want {
|
||||
t.Fatalf("事件不符:\n got %q\nwant %q", strings.Join(got, "|"), want)
|
||||
}
|
||||
if evs := diff(next, next); len(evs) != 0 {
|
||||
t.Fatalf("快照相同不应产生事件: %v", evs)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user