From ecc8328512e631347c3c5aac8545c94d86fb873c Mon Sep 17 00:00:00 2001 From: "Snow W. Lee (Sungwon)" Date: Wed, 29 Jul 2026 17:09:20 +0900 Subject: [PATCH] fix(shares): say when "the latest version" was (BEA-31) (#76) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A /s/ page promises the reader it always shows the latest version, then never says when latest was — so a stranger can't tell whether a living wiki page is from today or last March. Print FileInfo.Time, already in hand at the point handleShared renders, as a muted line above the content: human date, precise RFC3339 in the title attribute. Zero time prints nothing rather than a 1970 date. Markdown shells only. The .html/.htm branch stays a raw io.Copy and binaries stay untouched — the new test asserts byte equality for both. Co-authored-by: Claude Opus 5 (1M context) --- internal/webapp/shares.go | 20 ++++++++-- internal/webapp/shares_test.go | 68 ++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 3 deletions(-) diff --git a/internal/webapp/shares.go b/internal/webapp/shares.go index 247048b..dcd2761 100644 --- a/internal/webapp/shares.go +++ b/internal/webapp/shares.go @@ -370,7 +370,7 @@ func (s *Server) handleShared(w http.ResponseWriter, r *http.Request) { return } w.Header().Set("Content-Type", "text/html; charset=utf-8") - fmt.Fprintf(w, sharedMarkdownShell, html.EscapeString(path.Base(sh.Path)), body) + fmt.Fprintf(w, sharedMarkdownShell, html.EscapeString(path.Base(sh.Path)), updatedStamp(fi.Time), body) case ".html", ".htm": w.Header().Set("Content-Type", "text/html; charset=utf-8") io.Copy(w, rc) @@ -381,7 +381,20 @@ func (s *Server) handleShared(w http.ResponseWriter, r *http.Request) { } } +// updatedStamp renders the "how old is this?" line a share page owes its +// reader — the link promises the latest version, so it has to say when latest +// was. Zero time (a source that doesn't know) prints nothing rather than 1970. +func updatedStamp(t time.Time) string { + if t.IsZero() { + return "" + } + t = t.UTC() + return fmt.Sprintf(`
Last updated %s
`, + html.EscapeString(t.Format(time.RFC3339)), html.EscapeString(t.Format("2 Jan 2006"))) +} + // sharedMarkdownShell wraps rendered markdown in a minimal readable page. +// Verbs, in order: title, updated stamp, body. const sharedMarkdownShell = ` %s %s +.updated{font-size:12.5px;color:#57606a;margin-bottom:28px} +@media (prefers-color-scheme: dark){footer.bdrive{border-color:#3a3a44;color:#888}.updated{color:#888}} +%s%s
Shared with BearDrive — synced files for AI agent teams
` diff --git a/internal/webapp/shares_test.go b/internal/webapp/shares_test.go index 38dae92..072fafe 100644 --- a/internal/webapp/shares_test.go +++ b/internal/webapp/shares_test.go @@ -3,6 +3,7 @@ package webapp import ( "bytes" "encoding/json" + "fmt" "net/http" "net/http/httptest" "path/filepath" @@ -365,6 +366,73 @@ func TestShareListAPIsAreStable(t *testing.T) { // helpers +// TestShareLastUpdatedStamp: a share page promises the latest version, so a +// markdown page says when latest was — and nothing else gets injected into. +func TestShareLastUpdatedStamp(t *testing.T) { + srv, p, _, f, h := shareHub(t) + const rawHTML = "

Q3

" + const pdf = "%PDF-1.4 fake\n" + f.put("dev1", "wiki/deck.pdf", pdf) + + // markdown: the stamp is the FILE's time, human date + precise title= + when := time.Date(2026, 3, 14, 9, 26, 53, 0, time.UTC) + f.putAt("dev1", "wiki/notes.md", "# Notes\n\nhello **team**", when) + token, _ := authedShare(t, srv, h, p.ID, "wiki/notes.md") + rec := do(t, h, "GET", "/s/"+token, nil) + body := rec.Body.String() + if !strings.Contains(body, "Last updated 14 Mar 2026") { + t.Fatalf("no last-updated stamp: %s", body) + } + if !strings.Contains(body, `title="2026-03-14T09:26:53Z"`) { + t.Fatalf("no precise timestamp: %s", body) + } + // ...and it sits above the content, not after it + if strings.Index(body, "Last updated") > strings.Index(body, "team") { + t.Fatal("stamp must render before the content") + } + // the sandbox + footer survive + if csp := rec.Header().Get("Content-Security-Policy"); csp != "sandbox allow-scripts allow-popups" { + t.Fatalf("CSP = %q", csp) + } + if rec.Header().Get("X-Content-Type-Options") != "nosniff" || rec.Header().Get("Referrer-Policy") != "no-referrer" { + t.Fatalf("share headers = %v", rec.Header()) + } + if !strings.Contains(body, "Shared with