Serve the TUI's screens as a web app
money serve puts the seven screens in a browser: accounts, transactions, the report with its period axis and sort, the rule builder with its live preview and edit-in-place, the rules list, the transfer builder with its tolerance preview, and the transfers list. Import and retag are buttons in the header. It is one binary: the page is plain HTML, CSS and JavaScript embedded with go:embed, with no framework and no build step. It is a second frontend, not a second implementation. Amounts are formatted on the server, the rule preview is matched by glob.Match there, and the transfer preview runs transfers.Analyze, so the browser only lays out answers and cannot drift from the TUI on what a rule catches or what a pair costs. The server outlives hand edits to rules.toml in a way the TUI does not, so retag and import re-read it first, as a fresh `money retag` or `money import` would, and the overview says when the file on disk no longer matches what the index was derived from. Edits and deletes still go by position, but carry the rule they showed and are refused unless rules.toml still holds exactly that there; a position from a stale tab could otherwise name a different rule. An edit takes the type pattern from the rule on disk, never from the request. There is no authentication, by request. It listens on loopback unless --addr says otherwise, and writes must be sent as JSON so a cross-site form cannot post to it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,7 @@ internal/rules applies ordered rules, writing rule_tag
|
||||
internal/transfers pairs the two legs of a movement, writing the transfers table
|
||||
internal/report per-tag aggregation
|
||||
internal/tui Bubble Tea models
|
||||
internal/web `money serve`: JSON API + embedded single page (static/)
|
||||
```
|
||||
|
||||
## Invariants
|
||||
@@ -224,6 +225,28 @@ back, and `refreshRulePreview` keeps the ones the new glob stops catching on
|
||||
screen marked `−` instead of dropping them silently, because giving one up is
|
||||
the decision being made.
|
||||
|
||||
**The web app is a second frontend, not a second implementation.**
|
||||
`internal/web` ports the TUI's screens over HTTP, and everything that decides
|
||||
something stays in Go: amounts are formatted server-side (`money` is text plus
|
||||
a sign, never a number the browser could add up), the rule builder's preview is
|
||||
matched by `glob.Match` on the server rather than re-implemented in JS, and the
|
||||
transfer preview runs `transfers.Analyze`. Keep it that way — a JS glob or a
|
||||
JS sum is a second copy of a rule that would drift from the first.
|
||||
`static/app.js` only lays out what the API returns.
|
||||
|
||||
The web server outlives edits to rules.toml in a way the TUI does not, so
|
||||
`/api/retag` and `/api/import` re-read it first (import also re-reads the
|
||||
account folders), and `/api/overview` reports `stale` when the file on disk no
|
||||
longer equals what the engines hold. Every edit and delete sends the rule or
|
||||
transfer it showed alongside the position, and is refused with 409 unless
|
||||
rules.toml still holds exactly that there — a position from a stale page can
|
||||
otherwise name a different rule. An edit takes `Type` from the rule *on disk*,
|
||||
never from the request, which is the web side of `TestRuleEditKeepsTypePattern`.
|
||||
|
||||
There is no auth by design (`--addr` defaults to loopback). Non-GET requests
|
||||
must be `application/json`, which is what keeps a cross-site form from posting
|
||||
to it; do not relax that without putting something else in its place.
|
||||
|
||||
## Adding a bank parser
|
||||
|
||||
Implement `parser.Parser` and call `parser.Register` from an `init`. Nothing
|
||||
|
||||
Reference in New Issue
Block a user