rastrillo / idear Public

Clone
git clone https://amadan.net/rastrillo/idear

Plain git — no account needed to clone.

Download

Download this file

1# The example board
2
3A complete app on rastrillo + idear: a shared message board whose roster is
4idear's. It is the worked reference `../SKILL.md` points at, and it is part
5of the `amadan.net/rastrillo/idear` module rather than a submodule — a
6nested module would need a `replace` directive pointing at its own parent,
7which is exactly the thing an app must never write.
8
9## Run it
10
11```sh
12BOARD_SEED=1 go run ./example -addr 127.0.0.1:8080 -db /tmp/board.db
13```
14
15Then sign in at <http://127.0.0.1:8080/signin> as one of the seeded
16accounts, all with the password `demo-password`:
17
18| Address | Role | What you can do |
19| ------------------- | ------ | ------------------------------------------------- |
20| `ada@example.test` | Owner | everything, including transferring ownership |
21| `kim@example.test` | Admin | invite and manage **Members** only |
22| `sam@example.test` | Member | read the roster; post; nothing else |
23
24Three accounts at three roles, because a role gate you cannot click on is a
25role gate nobody checks. Sign in as Kim and the role selector offers Member
26and nothing else; sign in as Sam and the members page offers no controls at
27all.
28
29`BOARD_ORIGIN` sets the external origin (it decides the CSRF check and the
30cookie attributes); `BOARD_NAME` sets the instance's display name on the
31public invitation page. Both default loudly.
32
33There is no mail server here, so `HandlerConfig.Deliver` is nil and idear
34puts the invitation **link itself** in the flash notice shown to the admin
35who minted it. Copy it out of the page. A deployed app sets `Deliver` and
36keeps the token out of the browser entirely.
37
38## What to read
39
40| File | What it shows |
41| -------------- | ------------------------------------------------------------------- |
42| `app.go` | the whole wiring, in the order it has to happen, with the reasons |
43| `models.go` | the app's own `User`, `BootSchema`, and a seed through the real flows |
44| `render.go` | the two idear render callbacks, and `idear.TokenFrom` in `RenderSignup` |
45| `handlers.go` | `idear.From(r)`, and an app route gated on Admin |
46| `app_test.go` | the whole flow through real HTTP — the thing to copy |
47
48Four things in here are load-bearing rather than stylistic, and each has a
49comment at the site saying so:
50
51- **`/` is behind `Require`.** Under the password plugin, deactivation is
52 enforced per request by `Require`, **not** at sign-in — `password.Signin`
53 runs Lookup → Verify → mint with no idear involvement. An ungated landing
54 page is a page a removed member can still read.
55- **`POST /signup` is wrapped in `rs.CarryToken`.** Without it the
56 invitation token never reaches admission and every invited signup is
57 refused.
58- **One 404 renderer**, bound once and handed to both `idear.Config.NotFound`
59 and chi's own `NotFound`. Two of them is a membership oracle, and it is
60 the one misconfiguration idear cannot detect at runtime.
61- **`renderSignup` seeds its hidden `invite` field from
62 `idear.TokenFrom(r)`.** `password.PageData` has nowhere to carry a token,
63 so without this a signup that fails validation re-renders a form with an
64 empty field and the invitee's *second* attempt is refused. If you rewrite
65 this page — the likeliest thing to do to it — keep that line.
66