commit 5117da8633af46002d89a255a3d3edb067deea72 equwal <truex@equwal.com> 2026-09-22 18:33:33 -0700 Merge master: the billing API for the add-on, and the terms of sale The routes of both branches stay. The account page keeps the state text of master (server.state) and also sets page.Active for the link to the bookmarks.
README.md | 29 +++++++++- main.go | 164 +++++++++++++++++++++++++++++++++++++++++++++++++++------ pages.html | 36 ++++++++++++- server_test.go | 131 ++++++++++++++++++++++++++++++++++++++++++++- stripe.go | 16 +++--- 5 files changed, 348 insertions(+), 28 deletions(-)
diff --cc main.go index a7b992f,8084f8a..890ddda --- a/main.go +++ b/main.go @@@ -163,14 -167,9 +167,17 @@@ func (s *server) routes() http.Handler m.HandleFunc("POST /api/login", s.apiLogin) m.HandleFunc("POST /api/logout", s.apiLogout) m.HandleFunc("POST /api/sync", s.sync) + m.HandleFunc("GET /api/account", s.apiAccount) + m.HandleFunc("POST /api/checkout", s.apiCheckout) + m.HandleFunc("POST /api/portal", s.apiPortal) + m.HandleFunc("GET /bookmarks", s.bookmarks) + m.HandleFunc("POST /bookmarks/add", s.addBookmark) + m.HandleFunc("GET /bookmarks/edit", s.editBookmark) + m.HandleFunc("POST /bookmarks/edit", s.saveBookmark) + m.HandleFunc("GET /bookmarks/file", s.bookmarkFile) + m.HandleFunc("POST /bookmarks/file", s.saveFile) + m.HandleFunc("POST /bookmarks/import", s.importFile) + m.HandleFunc("GET /bookmarks.txt", s.download) return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { h := w.Header() h.Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; "+ @@@ -431,7 -433,13 +444,14 @@@ func (s *server) showAccount(w http.Res p := s.page("Your account") p.Email, p.Error, p.Customer = a.Email, problem, a.Customer p.Unconfirmed = !s.verified(a) + p.Active = s.verified(a) && s.active(a) + p.State = s.state(a) + p.CanSubscribe = s.stripe != nil && !paid(a.Status) + s.render(w, status, "account", p) + } + + // state tells in one sentence if sync works for the account, and why. + func (s *server) state(a Account) string { left := s.trialLeft(a) switch { case s.stripe == nil: