marky

Lightweight bookmarking + plaintext archive
Log | Files | Refs | README | LICENSE

commit a8dd728f2e9a1be1ee223da5d646459ec7bcec98
parent 48502626fcbdc6ecd5b69b6073848766be2ef46e
Author: David Eisinger <[email protected]>
Date:   Thu,  9 Oct 2025 22:07:15 -0400

Remove /me endpoint

Diffstat:
MREADME.md | 1-
Minternal/api.go | 37++++++++++++++++++++++++++-----------
Minternal/api_auth_test.go | 20++++++++++----------
Minternal/web/app.js | 12+++---------
4 files changed, 39 insertions(+), 31 deletions(-)

diff --git a/README.md b/README.md @@ -38,7 +38,6 @@ Marky supports session cookies (for the web UI) and API tokens (for scripts, fee - POST `/signup` { username, password } - POST `/login` { username, password } - POST `/logout` - - GET `/me` returns `{ username }` when authenticated - API tokens: per-user credential used with HTTP Basic, header, or query param - GET `/api/token` → `{ present: true|false }` diff --git a/internal/api.go b/internal/api.go @@ -87,16 +87,6 @@ func NewHandlerWithConfig(db *DB, cfg HandlerConfig) (http.Handler, error) { ClearSession(w) w.WriteHeader(http.StatusNoContent) }) - mux.HandleFunc("/me", func(w http.ResponseWriter, r *http.Request) { - if uid, ok := userIDFromRequest(r, db); ok { - if u, err := db.GetUserByID(r.Context(), uid); err == nil && u != nil { - w.Header().Set("Content-Type", "application/json") - _ = json.NewEncoder(w).Encode(map[string]any{"username": u.Username}) - return - } - } - http.Error(w, "unauthorized", http.StatusUnauthorized) - }) // Bookmarklet generator and popup form mux.HandleFunc("/bookmarklet", func(w http.ResponseWriter, r *http.Request) { @@ -351,7 +341,7 @@ func NewHandlerWithConfig(db *DB, cfg HandlerConfig) (http.Handler, error) { return } if html, err := RenderPage("settings.html", cfg); err == nil { - // Embed API token directly for settings hydration + // Embed API token and current username for hydration if tok, _, err := db.GetAPIToken(r.Context(), uid); err == nil { inject := []byte("<script>window.MARKY_TOKEN=" + strconv.Quote(tok) + "</script>\n") marker := []byte("<script src=\"/app.js\"></script>") @@ -363,6 +353,17 @@ func NewHandlerWithConfig(db *DB, cfg HandlerConfig) (http.Handler, error) { html = out } } + if u, err := db.GetUserByID(r.Context(), uid); err == nil && u != nil { + inject := []byte("<script>window.MARKY_ME=" + strconv.Quote(u.Username) + "</script>\n") + marker := []byte("<script src=\"/app.js\"></script>") + if i := bytes.Index(html, marker); i >= 0 { + out := make([]byte, 0, len(html)+len(inject)) + out = append(out, html[:i]...) + out = append(out, inject...) + out = append(out, html[i:]...) + html = out + } + } html = applyCacheBust(html) w.Header().Set("Content-Type", "text/html; charset=utf-8") _, _ = w.Write(html) @@ -390,6 +391,20 @@ func NewHandlerWithConfig(db *DB, cfg HandlerConfig) (http.Handler, error) { } if u, err := db.GetUserByUsername(r.Context(), uname); err == nil && u != nil { if html, err := RenderPage("user.html", cfg); err == nil { + // Embed current username (if logged in) + if uid, ok := userIDFromRequest(r, db); ok && uid > 0 { + if cu, err := db.GetUserByID(r.Context(), uid); err == nil && cu != nil { + meInject := []byte("<script>window.MARKY_ME=" + strconv.Quote(cu.Username) + "</script>\n") + marker := []byte("<script src=\"/app.js\"></script>") + if i := bytes.Index(html, marker); i >= 0 { + out := make([]byte, 0, len(html)+len(meInject)) + out = append(out, html[:i]...) + out = append(out, meInject...) + out = append(out, html[i:]...) + html = out + } + } + } // Set <title> to "{username} | Marky" by replacing inner text only if ti := bytes.Index(html, []byte("<title>")); ti >= 0 { if tj := bytes.Index(html[ti:], []byte("</title>")); tj > 0 { diff --git a/internal/api_auth_test.go b/internal/api_auth_test.go @@ -9,7 +9,7 @@ import ( "testing" ) -func TestSignupLoginLogoutMe(t *testing.T) { +func TestSignupLoginLogoutSession(t *testing.T) { t.Parallel() db, err := OpenAtPath(t.Context(), t.TempDir()+"/authz.db") if err != nil { @@ -23,10 +23,10 @@ func TestSignupLoginLogoutMe(t *testing.T) { jar, _ := cookiejar.New(nil) client := &http.Client{Jar: jar} - // /me unauth - resp, _ := http.Get(srv.URL + "/me") + // /bookmarks unauth should be 401 + resp, _ := http.Get(srv.URL + "/bookmarks") if resp.StatusCode != http.StatusUnauthorized { - t.Fatalf("/me unauth got %d", resp.StatusCode) + t.Fatalf("/bookmarks unauth got %d", resp.StatusCode) } // signup @@ -39,10 +39,10 @@ func TestSignupLoginLogoutMe(t *testing.T) { } rs.Body.Close() - // /me auth - r2, _ := client.Get(srv.URL + "/me") + // /bookmarks auth should be 200 (empty list) + r2, _ := client.Get(srv.URL + "/bookmarks") if r2.StatusCode != http.StatusOK { - t.Fatalf("/me auth %d", r2.StatusCode) + t.Fatalf("/bookmarks auth %d", r2.StatusCode) } r2.Body.Close() @@ -54,10 +54,10 @@ func TestSignupLoginLogoutMe(t *testing.T) { } rs3.Body.Close() - // /me again - r4, _ := client.Get(srv.URL + "/me") + // /bookmarks after logout should be 401 again + r4, _ := client.Get(srv.URL + "/bookmarks") if r4.StatusCode != http.StatusUnauthorized { - t.Fatalf("/me after logout %d", r4.StatusCode) + t.Fatalf("/bookmarks after logout %d", r4.StatusCode) } r4.Body.Close() } diff --git a/internal/web/app.js b/internal/web/app.js @@ -23,19 +23,13 @@ createApp({ }, methods: { async initView() { - // Determine logged-in user - try { - const res = await fetch('/me'); - if (res.ok) this.me = await res.json(); - } catch {} + // Determine logged-in user from server-embedded data + if (window.MARKY_ME) this.me = { username: window.MARKY_ME }; const p = location.pathname.replace(/^\/+/, ''); const first = p.split('/')[0] || ''; // Root: show login/signup or redirect to user page if (!first) { - if (this.me && this.me.username) { - location.replace('/u:' + this.me.username); - return; - } + // Server redirects logged-in users to /u:{username}; render login/signup otherwise this.view = 'login'; return; }